Skip to content

Commit

Permalink
Fix default value indentation (#653)
Browse files Browse the repository at this point in the history
  • Loading branch information
J3173 authored Jan 28, 2025
1 parent a2a465e commit 78ff09f
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ public int getCurrentLineLength() {
return builder.getCurrentLineLength();
}

public int getIndentDepth() {
return builder.getIndentDepth();
}

public ODINMapper getOdinMapper() {
return odinMapper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.base.Joiner;
import com.nedap.archie.aom.*;
import com.nedap.archie.base.Cardinality;
import com.nedap.archie.base.OpenEHRBase;
import com.nedap.archie.rminfo.RMObjectMapperProvider;
import com.nedap.archie.serializer.adl.ADLDefinitionSerializer;
import org.openehr.odin.jackson.ODINMapper;
import org.openehr.odin.jackson.ODINPrettyPrinter;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -75,19 +78,23 @@ protected void buildDefaultValue(T cobj) {
builder.newUnindentedLine();
} else {
try {
String format;
String content;
if (rmObjectMapperProvider.getJsonObjectMapper() != null) {
format = DefaultValueContainer.JSON;
//the writerFor here makes sure type info gets output even for the root type
content = rmObjectMapperProvider.getJsonObjectMapper().writerFor(OpenEHRBase.class).writeValueAsString(cobj.getDefaultValue());
String content = rmObjectMapperProvider.getJsonObjectMapper().writerFor(OpenEHRBase.class).writeValueAsString(cobj.getDefaultValue());
serializeDefaultValueJson(content);
} else {
format = DefaultValueContainer.ODIN;
ObjectMapper objectMapper = rmObjectMapperProvider.getOutputOdinObjectMapper();
ObjectWriter objectWriter;
//the writerFor here makes sure type info gets output even for the root type
content = rmObjectMapperProvider.getOutputOdinObjectMapper().writerFor(OpenEHRBase.class).writeValueAsString(cobj.getDefaultValue());
if(objectMapper instanceof ODINMapper) {
// If the mapper is a ODINMapper, use the ODINPrettyPrinter to apply the correct indentation depth.
objectWriter = objectMapper.writerFor(OpenEHRBase.class).with(new ODINPrettyPrinter(builder.getIndentDepth()));
} else {
objectWriter = objectMapper.writerFor(OpenEHRBase.class);
}
String content = objectWriter.writeValueAsString(cobj.getDefaultValue());
serializeDefaultValueOdin(content);
}

serializeDefaultValueContainer(new DefaultValueContainer(format, content));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
Expand All @@ -97,28 +104,40 @@ protected void buildDefaultValue(T cobj) {
}

protected void serializeDefaultValueContainer(DefaultValueContainer container) {
// This method doesn't apply indentation to keep the original indentation in the container.
builder.tryNewLine();
if(container.getFormat() == null || container.getFormat().equalsIgnoreCase(DefaultValueContainer.ODIN)) {
builder.append("_default = ");
builder.newIndentedLine();
builder.appendMultipleLines(container.getContent());
builder.unindent();
builder.append(container.getContent());
} else {
builder.append("_default = (");
builder.append(container.getFormat());
builder.append(") <#");
if(container.getFormat().equalsIgnoreCase(DefaultValueContainer.JSON)) {
builder.newIndentedLine();
builder.appendMultipleLines(container.getContent());
builder.unindent();
} else {
//don't apply indentation - we have no idea of knowing whether this should indent or not for unknown formats
builder.append(container.getContent());
}
builder.append(container.getContent());
builder.append("#>");
}
}

protected void serializeDefaultValueJson(String jsonContent) {
builder.tryNewLine();
builder.append("_default = (");
builder.append(DefaultValueContainer.JSON);
builder.append(") <#");
builder.newIndentedLine();
// This will indent the JSON content to the correct level.
builder.appendMultipleLines(jsonContent);
builder.unindent();
builder.append("#>");
}

protected void serializeDefaultValueOdin(String odinContent) {
// This method doesn't apply indentation as it is unsafe to add extra indentation to ODIN.
// The ODIN might already be indented correctly by the ODINPrettyPrinter.
builder.tryNewLine();
builder.append("_default = ");
builder.append(odinContent);
}

private Set<String> getTupleAttributeNames(T cobj) {
return cobj.getAttributeTuples().stream()
.flatMap(cat -> cat.getMembers().stream())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nedap.archie.serializer.adl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.nedap.archie.adlparser.ADLParseException;
import com.nedap.archie.adlparser.ADLParser;
import com.nedap.archie.aom.Archetype;
Expand Down Expand Up @@ -107,10 +108,25 @@ public void ckm() throws ADLParseException {
}

@Test
public void defaultValues() throws Exception {
public void defaultValuesJson() throws Exception {
testDefaultValues(new ArchieRMObjectMapperProvider());
}

@Test
public void defaultValuesOdin() throws Exception {
testDefaultValues(new ArchieRMObjectMapperProvider() {
// Make this only return ODIN object mappers
@Override
public ObjectMapper getJsonObjectMapper() {
return null;
}
});
}

private void testDefaultValues(RMObjectMapperProvider rmObjectMapperProvider) throws Exception {
Archetype archetype = load("openEHR-EHR-CLUSTER.default_values.v1.adls");

Archetype result = roundtrip(archetype);
Archetype result = roundtrip(archetype, rmObjectMapperProvider);

CComplexObject startDvTextConstraint = archetype.itemAtPath("/items[id2]/value[id21]");
CComplexObject resultDvTextConstraint = result.itemAtPath("/items[id2]/value[id21]");
Expand All @@ -131,7 +147,10 @@ public void defaultValues() throws Exception {
}

private Archetype roundtrip(Archetype archetype) throws ADLParseException {
RMObjectMapperProvider rmObjectMapperProvider = new ArchieRMObjectMapperProvider();
return roundtrip(archetype, new ArchieRMObjectMapperProvider());
}

private Archetype roundtrip(Archetype archetype, RMObjectMapperProvider rmObjectMapperProvider) throws ADLParseException {
String serialized = ADLArchetypeSerializer.serialize(archetype, null, rmObjectMapperProvider);
logger.info(serialized);

Expand All @@ -154,6 +173,49 @@ private Archetype loadRoot(String resourceName) throws IOException, ADLParseExce
return new ADLParser(BuiltinReferenceModels.getMetaModels()).parse(ADLArchetypeSerializerTest.class.getClassLoader().getResourceAsStream(resourceName));
}

@Test
public void defaultValuesNoMetamodels() throws Exception {
Archetype archetype = new ADLParser(/* no metamodels */).parse(ADLArchetypeSerializerTest.class.getResourceAsStream("openEHR-EHR-CLUSTER.default_values.v1.adls"));

String serialized = ADLArchetypeSerializer.serialize(archetype, null, null);
logger.info(serialized);

ADLParser parser = new ADLParser(/* no metamodels */);
Archetype result = parser.parse(serialized);

assertTrue("roundtrip parsing should never cause errors: " + parser.getErrors().toString(), parser.getErrors().hasNoErrors());

String serialized2 = ADLArchetypeSerializer.serialize(result, null, null);
assertEquals("roundtrip serialization should be idempotent", serialized, serialized2);

CComplexObject startDvTextConstraint = archetype.itemAtPath("/items[id2]/value[id21]");
CComplexObject resultDvTextConstraint = result.itemAtPath("/items[id2]/value[id21]");

DefaultValueContainer startDvTextDefaultValueContainer = (DefaultValueContainer) startDvTextConstraint.getDefaultValue();
DefaultValueContainer resultDvTextDefaultValueContainer = (DefaultValueContainer) resultDvTextConstraint.getDefaultValue();

assertEquals(startDvTextDefaultValueContainer.getFormat(), resultDvTextDefaultValueContainer.getFormat());
assertEquals(startDvTextDefaultValueContainer.getContent(), resultDvTextDefaultValueContainer.getContent());

CComplexObject startDvCodedTextConstraint = archetype.itemAtPath("/items[id3]/value[id31]");
CComplexObject resultDvCodedTextConstraint = result.itemAtPath("/items[id3]/value[id31]");

DefaultValueContainer startDvCodedTextDefaultValueContainer = (DefaultValueContainer) startDvCodedTextConstraint.getDefaultValue();
DefaultValueContainer resultDvCodedTextDefaultValueContainer = (DefaultValueContainer) resultDvCodedTextConstraint.getDefaultValue();

assertEquals(startDvCodedTextDefaultValueContainer.getFormat(), resultDvCodedTextDefaultValueContainer.getFormat());
assertEquals(startDvCodedTextDefaultValueContainer.getContent(), resultDvCodedTextDefaultValueContainer.getContent());

CComplexObject startClusterConstraint = archetype.getDefinition();
CComplexObject resultClusterConstraint = result.getDefinition();

DefaultValueContainer startClusterDefaultValueContainer = (DefaultValueContainer) startClusterConstraint.getDefaultValue();
DefaultValueContainer resultClusterDefaultValueContainer = (DefaultValueContainer) resultClusterConstraint.getDefaultValue();

assertEquals(startClusterDefaultValueContainer.getFormat(), resultClusterDefaultValueContainer.getFormat());
assertEquals(startClusterDefaultValueContainer.getContent(), resultClusterDefaultValueContainer.getContent());
}

@Test
public void operationalTemplate() throws Exception {

Expand Down

0 comments on commit 78ff09f

Please sign in to comment.