Merge pull request #1391 from hapifhir/2023-08-gg-rendering-overhaul

2023 08 gg rendering overhaul
This commit is contained in:
Grahame Grieve 2023-08-15 19:38:18 +10:00 committed by GitHub
commit ebd92e4d5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 893 additions and 238 deletions

View File

@ -523,7 +523,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return doValidateCode(options, code, vs, false);
}
public ValidationResult doValidateCode(ValidationOptions options, Coding code, ValueSet vs, boolean implySystem) {
public ValidationResult doValidateCode(ValidationOptions options, Coding code, ValueSet vs, boolean inferSystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
ValidationResult res = null;
if (txCache != null)
@ -553,8 +553,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
try {
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(code);
if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
if (inferSystem)
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
if (options != null)
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);

View File

@ -914,7 +914,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(t.getCoding());
if (options.isGuessSystem()) {
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
}
if (vs != null) {
pIn.addParameter().setName("valueSet").setResource(vs);
@ -1030,7 +1030,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(code);
if (options.isGuessSystem()) {
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
}
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn, options);

View File

@ -11,6 +11,7 @@ import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.ChangeAnalysisState;
import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CapabilityStatement;
@ -52,7 +53,8 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
private ChangeAnalysisState changedContent = ChangeAnalysisState.Unknown;
private ChangeAnalysisState changedContentInterpretation = ChangeAnalysisState.Unknown;
protected Map<String, StructuralMatch<String>> metadata = new HashMap<>();
protected Map<String, StructuralMatch<String>> metadata = new HashMap<>();
private List<String> chMetadataFields;
public CanonicalResourceComparison(T left, T right) {
super(left.getId(), right.getId());
@ -133,8 +135,9 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
changedContentInterpretation = updateState(state, changedContentInterpretation);
}
public void updatedMetadataState(boolean state) {
public void updatedMetadataState(boolean state, List<String> chMetadataFields) {
changedMetadata = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedMetadata);
this.chMetadataFields = chMetadataFields;
}
public void updateDefinitionsState(boolean state) {
@ -149,6 +152,27 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
changedContentInterpretation = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedContentInterpretation);
}
public boolean anyUpdates() {
return changedMetadata.noteable() || changedDefinitions.noteable() || changedContent.noteable() || changedContentInterpretation.noteable();
}
public ChangeAnalysisState getChangedMetadata() {
return changedMetadata;
}
public ChangeAnalysisState getChangedDefinitions() {
return changedDefinitions;
}
public ChangeAnalysisState getChangedContent() {
return changedContent;
}
public ChangeAnalysisState getChangedContentInterpretation() {
return changedContentInterpretation;
}
@Override
protected String toTable() {
String s = "";
@ -198,30 +222,76 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
}
return (bc.length() == 0 ? "" : "Error Checking: "+bc.toString()+"; ")+ "Changed: "+b.toString();
}
public String getMetadataFieldsAsText() {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
if (chMetadataFields != null) {
for (String s : chMetadataFields) {
b.append(s);
}
}
return b.toString();
}
}
public CanonicalResourceComparer(ComparisonSession session) {
super(session);
}
protected boolean compareMetadata(CanonicalResource left, CanonicalResource right, Map<String, StructuralMatch<String>> comp, CanonicalResourceComparison<? extends CanonicalResource> res) {
protected boolean compareMetadata(CanonicalResource left, CanonicalResource right, Map<String, StructuralMatch<String>> comp, CanonicalResourceComparison<? extends CanonicalResource> res, List<String> changes) {
var changed = false;
changed = comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res) || changed;
if (session.getForVersion() == null) {
changed = comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res) || changed;
if (comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res)) {
changed = true;
changes.add("url");
}
changed = comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp, IssueSeverity.WARNING, res) || changed;
if (session.getForVersion() == null) {
changed = comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res) || changed;
if (comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res)) {
changed = true;
changes.add("version");
}
}
if (comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("name");
}
if (comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("title");
}
if (comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("status");
}
if (comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp, IssueSeverity.WARNING, res)) {
changed = true;
changes.add("experimental");
}
if (session.getForVersion() == null) {
if (comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("date");
}
}
if (comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("publisher");
}
if (comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res)) {
changed = true;
changes.add("description");
}
if (comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res)) {
changed = true;
changes.add("purpose");
}
if (comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("copyright");
}
if (compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction())) {
changed = true;
changes.add("jurisdiction");
}
changed = comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res) || changed;
changed = comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res) || changed;
changed = comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction()) || changed;
return changed;
}
@ -374,6 +444,41 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
return match.isDifferent();
}
@SuppressWarnings("rawtypes")
protected boolean comparePrimitivesWithTracking(String name, PrimitiveType l, PrimitiveType r, Map<String, StructuralMatch<String>> comp, IssueSeverity level, CanonicalResourceComparison<? extends CanonicalResource> res, Base parent, String version) {
StructuralMatch<String> match = null;
if (l.isEmpty() && r.isEmpty()) {
match = new StructuralMatch<>(null, null, null);
} else if (l.isEmpty()) {
match = new StructuralMatch<>(null, r.primitiveValue(), vmI(IssueSeverity.INFORMATION, "Added the item '"+r.primitiveValue()+"'", fhirType()+"."+name));
VersionComparisonAnnotation.markAdded(r, version);
} else if (r.isEmpty()) {
match = new StructuralMatch<>(l.primitiveValue(), null, vmI(IssueSeverity.INFORMATION, "Removed the item '"+l.primitiveValue()+"'", fhirType()+"."+name));
VersionComparisonAnnotation.markDeleted(parent, version, name, l);
} else if (!l.hasValue() && !r.hasValue()) {
match = new StructuralMatch<>(null, null, vmI(IssueSeverity.INFORMATION, "No Value", fhirType()+"."+name));
} else if (!l.hasValue()) {
match = new StructuralMatch<>(null, r.primitiveValue(), vmI(IssueSeverity.INFORMATION, "No Value on Left", fhirType()+"."+name));
VersionComparisonAnnotation.markAdded(r, version);
} else if (!r.hasValue()) {
match = new StructuralMatch<>(l.primitiveValue(), null, vmI(IssueSeverity.INFORMATION, "No Value on Right", fhirType()+"."+name));
VersionComparisonAnnotation.markDeleted(parent, version, name, l);
} else if (l.getValue().equals(r.getValue())) {
match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(), null);
} else {
VersionComparisonAnnotation.markChanged(r, version);
match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(), vmI(level, "Values Differ", fhirType()+"."+name));
if (level != IssueSeverity.NULL && res != null) {
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, fhirType()+"."+name, "Values for "+name+" differ: '"+l.primitiveValue()+"' vs '"+r.primitiveValue()+"'", level));
}
}
if (comp != null) {
comp.put(name, match);
}
return match.isDifferent();
}
protected abstract String fhirType();
public XhtmlNode renderMetadata(CanonicalResourceComparison<? extends CanonicalResource> comparison, String id, String prefix) throws FHIRException, IOException {

View File

@ -114,7 +114,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
cs1.setStatus(left.getStatus());
cs1.setDate(new Date());
compareMetadata(left, right, res.getMetadata(), res);
compareMetadata(left, right, res.getMetadata(), res, new ArrayList<>());
comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.ERROR, res);
compareCanonicalList("instantiates", left.getInstantiates(), right.getInstantiates(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getInstantiates(), cs1.getInstantiates());
compareCanonicalList("imports", left.getImports(), right.getImports(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getImports(), cs1.getImports());

View File

@ -10,7 +10,9 @@ import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
@ -31,6 +33,8 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
public class CodeSystemComparison extends CanonicalResourceComparison<CodeSystem> {
private StructuralMatch<PropertyComponent> properties;
private StructuralMatch<CodeSystemFilterComponent> filters;
private StructuralMatch<ConceptDefinitionComponent> combined;
private Map<String, String> propMap = new HashMap<>(); // right to left; left retains it's name
public CodeSystemComparison(CodeSystem left, CodeSystem right) {
@ -46,6 +50,14 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return combined;
}
public StructuralMatch<PropertyComponent> getProperties() {
return properties;
}
public StructuralMatch<CodeSystemFilterComponent> getFilters() {
return filters;
}
@Override
protected String abbreviation() {
return "cs";
@ -87,7 +99,6 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
if (right == null)
throw new DefinitionException("No CodeSystem provided (right)");
CodeSystemComparison res = new CodeSystemComparison(left, right);
session.identify(res);
CodeSystem cs = new CodeSystem();
@ -119,18 +130,29 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
cs1.setDate(new Date());
cs1.getProperty().addAll(cs.getProperty());
boolean ch = compareMetadata(left, right, res.getMetadata(), res);
ch = comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res) || ch;
ch = comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch;
res.updatedMetadataState(ch);
List<String> chMetadata = new ArrayList<>();
boolean ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata);
if (comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res)) {
ch = true;
chMetadata.add("versionNeeded");
}
if (comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("compositional");
}
res.updatedMetadataState(ch, chMetadata);
ch = false;
ch = comparePrimitives("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
ch = comparePrimitives("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
ch = comparePrimitives("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res);
ch = comparePrimitivesWithTracking("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res, right, session.getForVersion()) || ch;
ch = comparePrimitivesWithTracking("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res, right, session.getForVersion()) || ch;
ch = comparePrimitivesWithTracking("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res, right, session.getForVersion());
ch = compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept") || ch;
ch = compareProperties(left.getProperty(), right.getProperty(), res.getProperties(), res.getUnion().getProperty(), res.getIntersection().getProperty(), res.getUnion(), res.getIntersection(), res, "CodeSystem.property", right) || ch;
ch = compareFilters(left.getFilter(), right.getFilter(), res.getFilters(), res.getUnion().getFilter(), res.getIntersection().getFilter(), res.getUnion(), res.getIntersection(), res, "CodeSystem.filter", right) || ch;
ch = compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept", right) || ch;
res.updateDefinitionsState(ch);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -161,10 +183,81 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return null;
}
private boolean compareProperties(List<PropertyComponent> left, List<PropertyComponent> right, StructuralMatch<PropertyComponent> combined,
List<PropertyComponent> union, List<PropertyComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
boolean def = false;
List<PropertyComponent> matchR = new ArrayList<>();
for (PropertyComponent l : left) {
PropertyComponent r = findInList(right, l);
if (r == null) {
union.add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.PropertyComponent>(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
} else {
matchR.add(r);
PropertyComponent cdM = merge(l, r, res);
PropertyComponent cdI = intersect(l, r, res);
union.add(cdM);
intersection.add(cdI);
StructuralMatch<PropertyComponent> sm = new StructuralMatch<CodeSystem.PropertyComponent>(l, r);
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
def = true;
}
combined.getChildren().add(sm);
}
}
for (PropertyComponent r : right) {
if (!matchR.contains(r)) {
union.add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.PropertyComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;
}
private boolean compareFilters(List<CodeSystemFilterComponent> left, List<CodeSystemFilterComponent> right, StructuralMatch<CodeSystemFilterComponent> combined,
List<CodeSystemFilterComponent> union, List<CodeSystemFilterComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
boolean def = false;
List<CodeSystemFilterComponent> matchR = new ArrayList<>();
for (CodeSystemFilterComponent l : left) {
CodeSystemFilterComponent r = findInList(right, l);
if (r == null) {
union.add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.CodeSystemFilterComponent>(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
} else {
matchR.add(r);
CodeSystemFilterComponent cdM = merge(l, r, res);
CodeSystemFilterComponent cdI = intersect(l, r, res);
union.add(cdM);
intersection.add(cdI);
StructuralMatch<CodeSystemFilterComponent> sm = new StructuralMatch<CodeSystem.CodeSystemFilterComponent>(l, r);
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
def = true;
}
combined.getChildren().add(sm);
}
}
for (CodeSystemFilterComponent r : right) {
if (!matchR.contains(r)) {
union.add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.CodeSystemFilterComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;
}
private boolean compareConcepts(List<ConceptDefinitionComponent> left, List<ConceptDefinitionComponent> right, StructuralMatch<ConceptDefinitionComponent> combined,
List<ConceptDefinitionComponent> union, List<ConceptDefinitionComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path) {
boolean result = false;
List<ConceptDefinitionComponent> union, List<ConceptDefinitionComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
boolean def = false;
List<ConceptDefinitionComponent> matchR = new ArrayList<>();
for (ConceptDefinitionComponent l : left) {
ConceptDefinitionComponent r = findInList(right, l);
@ -172,6 +265,7 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
union.add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
} else {
matchR.add(r);
ConceptDefinitionComponent cdM = merge(l, r, csU.getProperty(), res);
@ -179,12 +273,12 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
union.add(cdM);
intersection.add(cdI);
StructuralMatch<ConceptDefinitionComponent> sm = new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(l, r);
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res)) {
result = true;
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
def = true;
}
combined.getChildren().add(sm);
if (compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept")) {
result = true;
if (compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept", r)) {
def = true;
}
}
}
@ -192,11 +286,22 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
if (!matchR.contains(r)) {
union.add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
combined.getChildren().add(new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return result;
return def;
}
private CodeSystemFilterComponent findInList(List<CodeSystemFilterComponent> list, CodeSystemFilterComponent item) {
for (CodeSystemFilterComponent t : list) {
if (t.getCode().equals(item.getCode())) {
return t;
}
}
return null;
}
private ConceptDefinitionComponent findInList(List<ConceptDefinitionComponent> list, ConceptDefinitionComponent item) {
for (ConceptDefinitionComponent t : list) {
@ -207,27 +312,58 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return null;
}
private boolean compare(List<ValidationMessage> msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res) {
private PropertyComponent findInList(List<PropertyComponent> list, PropertyComponent item) {
for (PropertyComponent t : list) {
if (t.getCode().equals(item.getCode())) {
return t;
}
}
return null;
}
private boolean compare(List<ValidationMessage> msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res, Base parent) {
boolean result = false;
result = compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res) || result;
result = compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res) || result;
result = compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res, parent, l.getDisplayElement(), r.getDisplayElement()) || result;
result = compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res, parent, l.getDefinitionElement(), r.getDefinitionElement()) || result;
// todo: designations, properties
return result;
}
private boolean compareStrings(String path, List<ValidationMessage> msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res) {
private boolean compare(List<ValidationMessage> msgs, PropertyComponent l, PropertyComponent r, String path, CodeSystemComparison res, Base parent) {
boolean result = false;
result = compareStrings(path, msgs, l.getUri(), r.getUri(), "uri", IssueSeverity.WARNING, res, parent, l.getUriElement(), r.getUriElement()) || result;
result = compareStrings(path, msgs, l.hasType() ? l.getType().toCode() : null, r.hasType() ? r.getType().toCode() : null, "type", IssueSeverity.ERROR, res, parent, l.getTypeElement(), r.getTypeElement()) || result;
result = compareStrings(path, msgs, l.getDescription(), r.getDescription(), "description", IssueSeverity.WARNING, res, parent, l.getDescriptionElement(), r.getDescriptionElement()) || result;
return result;
}
private boolean compare(List<ValidationMessage> msgs, CodeSystemFilterComponent l, CodeSystemFilterComponent r, String path, CodeSystemComparison res, Base parent) {
boolean result = false;
result = compareStrings(path, msgs, l.getDescription(), r.getDescription(), "description", IssueSeverity.WARNING, res, parent, l.getDescriptionElement(), r.getDescriptionElement()) || result;
// todo: repeating
// result = compareStrings(path, msgs, l.hasOperator() ? l.getOperator().toCode() : null, r.hasType() ? r.getType().toCode() : null, "type", IssueSeverity.ERROR, res, parent, l.getTypeElement(), r.getTypeElement()) || result;
result = compareStrings(path, msgs, l.getValue(), r.getValue(), "value", IssueSeverity.WARNING, res, parent, l.getValueElement(), r.getValueElement()) || result;
return result;
}
private boolean compareStrings(String path, List<ValidationMessage> msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res, Base parent, Base l, Base r) {
if (!Utilities.noString(right)) {
if (Utilities.noString(left)) {
msgs.add(vmI(level, "Value for "+name+" added", path));
msgs.add(vmI(level, "Value for "+name+" added", path));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
return true;
} else if (!left.equals(right)) {
if (level != IssueSeverity.NULL) {
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+left+"' vs '"+right+"'", level));
}
msgs.add(vmI(level, name+" changed from left to right", path));
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
return true;
}
} else if (!Utilities.noString(left)) {
msgs.add(vmI(level, "Value for "+name+" removed", path));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
return true;
}
return false;
@ -246,6 +382,23 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return cd;
}
private PropertyComponent merge(PropertyComponent l, PropertyComponent r, CodeSystemComparison res) {
PropertyComponent cd = l.copy();
if (!l.hasDescription() && r.hasDescription()) {
cd.setDescription(r.getDescription());
}
return cd;
}
private CodeSystemFilterComponent merge(CodeSystemFilterComponent l, CodeSystemFilterComponent r, CodeSystemComparison res) {
CodeSystemFilterComponent cd = l.copy();
if (!l.hasDescription() && r.hasDescription()) {
cd.setDescription(r.getDescription());
}
return cd;
}
private ConceptDefinitionComponent intersect(ConceptDefinitionComponent l, ConceptDefinitionComponent r, CodeSystemComparison res) {
ConceptDefinitionComponent cd = l.copy();
if (l.hasDisplay() && !r.hasDisplay()) {
@ -259,6 +412,22 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return cd;
}
private PropertyComponent intersect(PropertyComponent l, PropertyComponent r, CodeSystemComparison res) {
PropertyComponent cd = l.copy();
if (l.hasDescription() && !r.hasDescription()) {
cd.setDescription(null);
}
return cd;
}
private CodeSystemFilterComponent intersect(CodeSystemFilterComponent l, CodeSystemFilterComponent r, CodeSystemComparison res) {
CodeSystemFilterComponent cd = l.copy();
if (l.hasDescription() && !r.hasDescription()) {
cd.setDescription(null);
}
return cd;
}
private void mergeDesignations(ConceptDefinitionComponent cd, ConceptDefinitionComponent l, ConceptDefinitionComponent r) {
for (ConceptDefinitionDesignationComponent td : l.getDesignation()) {
if (hasDesignation(td, r.getDesignation())) {
@ -341,7 +510,8 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
private ConceptPropertyComponent findRightProp(List<ConceptPropertyComponent> rightProperties, ConceptPropertyComponent lp, CodeSystemComparison res) {
for (ConceptPropertyComponent p : rightProperties) {
if (res.getPropMap().get(p.getCode()).equals(lp.getCode())) {
String rp = res.getPropMap().get(p.getCode());
if (rp != null && rp.equals(lp.getCode())) {
return p;
}
}

View File

@ -107,9 +107,9 @@ public class ComparisonSession {
throw new FHIRException("Unable to compare resources of type "+left.fhirType()+" and "+right.fhirType());
}
} catch (Throwable e) {
if (debug) {
// if (debug) {
e.printStackTrace();
}
// }
ResourceComparer.PlaceHolderComparison csc = new ResourceComparer.PlaceHolderComparison(left, right, e);
compares.put(key, csc);
return csc;

View File

@ -5,10 +5,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.CanonicalResourceComparison;
import org.hl7.fhir.r5.comparison.ValueSetComparer.ValueSetComparison;
import org.hl7.fhir.r5.conformance.profile.BindingResolution;
import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
@ -17,6 +19,7 @@ import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.ElementDefinition;
@ -143,20 +146,39 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
sd1.setStatus(left.getStatus());
sd1.setDate(new Date());
compareMetadata(left, right, res.getMetadata(), res);
comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.WARNING, res);
comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.WARNING, res);
comparePrimitives("abstract", left.getAbstractElement(), right.getAbstractElement(), res.getMetadata(), IssueSeverity.WARNING, res);
comparePrimitives("type", left.getTypeElement(), right.getTypeElement(), res.getMetadata(), IssueSeverity.ERROR, res);
comparePrimitives("baseDefinition", left.getBaseDefinitionElement(), right.getBaseDefinitionElement(), res.getMetadata(), IssueSeverity.ERROR, res);
List<String> chMetadata = new ArrayList<>();
boolean ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata);
if (comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("fhirVersion");
}
if (comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("kind");
}
if (comparePrimitives("abstract", left.getAbstractElement(), right.getAbstractElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("abstract");
}
res.updatedMetadataState(ch, chMetadata);
ch = false;
ch = comparePrimitives("type", left.getTypeElement(), right.getTypeElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
ch = comparePrimitives("baseDefinition", left.getBaseDefinitionElement(), right.getBaseDefinitionElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
if (left.getType().equals(right.getType())) {
DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left);
DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right);
DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left, false);
DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right, false);
StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(left, ln.current()), new ElementDefinitionNode(right, rn.current()));
compareElements(res, sm, ln.path(), null, ln, rn);
res.combined = sm;
ln = new DefinitionNavigator(session.getContextLeft(), left, true);
rn = new DefinitionNavigator(session.getContextRight(), right, true);
ch = compareDiff(ln.path(), null, ln, rn) || ch;
// we don't preserve the differences - we only want the annotations
}
res.updateDefinitionsState(ch);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -173,12 +195,14 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
throw new DefinitionException("StructureDefinition snapshot is empty ("+name+": "+sd.getName()+")");
}
private void compareElements(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException {
private boolean compareElements(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException {
assert(path != null);
assert(left != null);
assert(right != null);
assert(left.path().equals(right.path()));
boolean def = false;
if (session.isDebug()) {
System.out.println("Compare elements at "+path);
}
@ -196,7 +220,6 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
if (sliceName != null)
subset.setSliceName(sliceName);
subset.getRepresentation().addAll(left.current().getRepresentation()); // can't be bothered even testing this one
subset.setDefaultValue(left.current().getDefaultValue());
subset.setMeaningWhenMissing(left.current().getMeaningWhenMissing());
@ -205,10 +228,20 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
// descriptive properties from ElementDefinition - merge them:
subset.setLabel(mergeText(comp, res, path, "label", left.current().getLabel(), right.current().getLabel(), false));
comparePrimitivesWithTracking("label", left.current().getLabelElement(), right.current().getLabelElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion());
subset.setShort(mergeText(comp, res, path, "short", left.current().getShort(), right.current().getShort(), false));
def = comparePrimitivesWithTracking("short", left.current().getShortElement(), right.current().getShortElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.setDefinition(mergeText(comp, res, path, "definition", left.current().getDefinition(), right.current().getDefinition(), false));
def = comparePrimitivesWithTracking("definition", left.current().getDefinitionElement(), right.current().getDefinitionElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.setComment(mergeText(comp, res, path, "comments", left.current().getComment(), right.current().getComment(), false));
def = comparePrimitivesWithTracking("comment", left.current().getCommentElement(), right.current().getCommentElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.setRequirements(mergeText(comp, res, path, "requirements", left.current().getRequirements(), right.current().getRequirements(), false));
def = comparePrimitivesWithTracking("requirements", left.current().getRequirementsElement(), right.current().getRequirementsElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.getCode().addAll(mergeCodings(left.current().getCode(), right.current().getCode()));
subset.getAlias().addAll(mergeStrings(left.current().getAlias(), right.current().getAlias()));
subset.getMapping().addAll(mergeMappings(left.current().getMapping(), right.current().getMapping()));
@ -220,14 +253,18 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
}
subset.setMustSupport(left.current().getMustSupport() || right.current().getMustSupport());
def = comparePrimitivesWithTracking("mustSupport", left.current().getMustSupportElement(), right.current().getMustSupportElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
ElementDefinition superset = subset.copy();
def = comparePrimitivesWithTracking("min", left.current().getMinElement(), right.current().getMinElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("max", left.current().getMaxElement(), right.current().getMaxElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
// compare and intersect
int leftMin = left.current().getMin();
int rightMin = right.current().getMin();
int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(left.current().getMax());
int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(right.current().getMax());
int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Utilities.parseInt(left.current().getMax(), -1);
int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Utilities.parseInt(right.current().getMax(), -1);
checkMinMax(comp, res, path, leftMin, rightMin, leftMax, rightMax);
superset.setMin(unionMin(leftMin, rightMin));
@ -252,7 +289,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
comp.getUnion().getSnapshot().getElement().add(superset);
// add the children
compareChildren(comp, res, path, left, right);
def = compareChildren(comp, res, path, left, right) || def;
//
// // now process the slices
// if (left.current().hasSlicing() || right.current().hasSlicing()) {
@ -312,10 +349,13 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
//
// // TODO Auto-generated method stub
// return null;
return def;
}
private void compareChildren(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError {
private boolean compareChildren(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError {
boolean def = false;
List<DefinitionNavigator> lc = left.children();
List<DefinitionNavigator> rc = right.children();
// it's possible that one of these profiles walks into a data type and the other doesn't
@ -336,7 +376,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
matchR.add(r);
StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(l.getStructure(), l.current()), new ElementDefinitionNode(r.getStructure(), r.current()));
res.getChildren().add(sm);
compareElements(comp, sm, l.path(), null, l, r);
def = compareElements(comp, sm, l.path(), null, l, r) || def;
}
}
for (DefinitionNavigator r : rc) {
@ -345,6 +385,127 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
res.getChildren().add(new StructuralMatch<ElementDefinitionNode>(vmI(IssueSeverity.INFORMATION, "Added this element", path), new ElementDefinitionNode(r.getStructure(), r.current())));
}
}
return def;
}
private boolean compareDiff(String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException {
assert(path != null);
assert(left != null);
assert(right != null);
assert(left.path().equals(right.path()));
boolean def = false;
// not allowed to be different:
// ruleEqual(comp, res, left.current().getDefaultValue(), right.current().getDefaultValue(), "defaultValue", path);
// ruleEqual(comp, res, left.current().getMeaningWhenMissingElement(), right.current().getMeaningWhenMissingElement(), "meaningWhenMissing", path);
// ruleEqual(comp, res, left.current().getIsModifierElement(), right.current().getIsModifierElement(), "isModifier", path); - this check belongs in the core
// ruleEqual(comp, res, left.current().getIsSummaryElement(), right.current().getIsSummaryElement(), "isSummary", path); - so does this
// descriptive properties from ElementDefinition - merge them:
comparePrimitivesWithTracking("label", left.current().getLabelElement(), right.current().getLabelElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion());
def = comparePrimitivesWithTracking("short", left.current().getShortElement(), right.current().getShortElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("definition", left.current().getDefinitionElement(), right.current().getDefinitionElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("comment", left.current().getCommentElement(), right.current().getCommentElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("requirements", left.current().getRequirementsElement(), right.current().getRequirementsElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("mustSupport", left.current().getMustSupportElement(), right.current().getMustSupportElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("min", left.current().getMinElement(), right.current().getMinElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("max", left.current().getMaxElement(), right.current().getMaxElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
// add the children
def = compareDiffChildren(path, left, right) || def;
//
// // now process the slices
// if (left.current().hasSlicing() || right.current().hasSlicing()) {
// assert sliceName == null;
// if (isExtension(left.path()))
// return compareExtensions(outcome, path, superset, subset, left, right);
// // return true;
// else {
// ElementDefinitionSlicingComponent slicingL = left.current().getSlicing();
// ElementDefinitionSlicingComponent slicingR = right.current().getSlicing();
// // well, this is tricky. If one is sliced, and the other is not, then in general, the union just ignores the slices, and the intersection is the slices.
// if (left.current().hasSlicing() && !right.current().hasSlicing()) {
// // the super set is done. Any restrictions in the slices are irrelevant to what the super set says, except that we're going sum up the value sets if we can (for documentation purposes) (todo)
// // the minimum set is the slicing specified in the slicer
// subset.setSlicing(slicingL);
// // stick everything from the right to do with the slices to the subset
// copySlices(outcome.subset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), left.slices());
// } else if (!left.current().hasSlicing() && right.current().hasSlicing()) {
// // the super set is done. Any restrictions in the slices are irrelevant to what the super set says, except that we're going sum up the value sets if we can (for documentation purposes) (todo)
// // the minimum set is the slicing specified in the slicer
// subset.setSlicing(slicingR);
// // stick everything from the right to do with the slices to the subset
// copySlices(outcome.subset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), right.slices());
// } else if (isTypeSlicing(slicingL) || isTypeSlicing(slicingR)) {
// superset.getSlicing().setRules(SlicingRules.OPEN).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this");
// subset.getSlicing().setRules(slicingL.getRules() == SlicingRules.CLOSED || slicingR.getRules() == SlicingRules.CLOSED ? SlicingRules.OPEN : SlicingRules.CLOSED).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this");
//
// // the superset is the union of the types
// // the subset is the intersection of them
// List<DefinitionNavigator> handled = new ArrayList<>();
// for (DefinitionNavigator t : left.slices()) {
// DefinitionNavigator r = findMatchingSlice(right.slices(), t);
// if (r == null) {
// copySlice(outcome.superset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), t);
// } else {
// handled.add(r);
// ret = compareElements(outcome, path+":"+t.current().getSliceName(), t, r, t.current().getSliceName()) && ret;
// }
// }
// for (DefinitionNavigator t : right.slices()) {
// if (!handled.contains(t)) {
// copySlice(outcome.superset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), t);
// }
// }
// } else if (slicingMatches(slicingL, slicingR)) {
// // if it's the same, we can try matching the slices - though we might have to give up without getting matches correct
// // there amy be implied consistency we can't reason about
// throw new DefinitionException("Slicing matches but is not handled yet at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+")");
// } else {
// // if the slicing is different, we can't compare them - or can we?
// throw new DefinitionException("Slicing doesn't match at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+" / "+ProfileUtilities.summarizeSlicing(slicingR)+")");
// }
// }
// // todo: name
// }
// return ret;
//
// // TODO Auto-generated method stub
// return null;
return def;
}
private boolean compareDiffChildren(String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError {
boolean def = false;
List<DefinitionNavigator> lc = left.children();
List<DefinitionNavigator> rc = right.children();
// it's possible that one of these profiles walks into a data type and the other doesn't
// if it does, we have to load the children for that data into the profile that doesn't
// walk into it
if (lc.isEmpty() && !rc.isEmpty() && right.current().getType().size() == 1 && left.hasTypeChildren(right.current().getType().get(0), left.getStructure()))
lc = left.childrenFromType(right.current().getType().get(0), right.getStructure());
if (rc.isEmpty() && !lc.isEmpty() && left.current().getType().size() == 1 && right.hasTypeChildren(left.current().getType().get(0), right.getStructure()))
rc = right.childrenFromType(left.current().getType().get(0), left.getStructure());
List<DefinitionNavigator> matchR = new ArrayList<>();
for (DefinitionNavigator l : lc) {
DefinitionNavigator r = findInList(rc, l);
if (r == null) {
// todo
} else {
def = compareDiff(l.path(), null, l, r) || def;
}
}
for (DefinitionNavigator r : rc) {
if (!matchR.contains(r)) {
// todo
}
}
return def;
}
private DefinitionNavigator findInList(List<DefinitionNavigator> rc, DefinitionNavigator l) {
@ -356,18 +517,18 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
return null;
}
private void ruleEqual(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, DataType vLeft, DataType vRight, String name, String path) throws IOException {
if (vLeft == null && vRight == null) {
// nothing
} else if (vLeft == null) {
vm(IssueSeverity.ERROR, "Added "+name, path, comp.getMessages(), res.getMessages());
} else if (vRight == null) {
vm(IssueSeverity.ERROR, "Removed "+name, path, comp.getMessages(), res.getMessages());
} else if (!Base.compareDeep(vLeft, vRight, false)) {
vm(IssueSeverity.ERROR, name+" must be the same ("+toString(vLeft, true)+"/"+toString(vRight, false)+")", path, comp.getMessages(), res.getMessages());
}
}
// private void ruleEqual(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, DataType vLeft, DataType vRight, String name, String path) throws IOException {
// if (vLeft == null && vRight == null) {
// // nothing
// } else if (vLeft == null) {
// vm(IssueSeverity.ERROR, "Added "+name, path, comp.getMessages(), res.getMessages());
// } else if (vRight == null) {
// vm(IssueSeverity.ERROR, "Removed "+name, path, comp.getMessages(), res.getMessages());
// } else if (!Base.compareDeep(vLeft, vRight, false)) {
// vm(IssueSeverity.ERROR, name+" must be the same ("+toString(vLeft, true)+"/"+toString(vRight, false)+")", path, comp.getMessages(), res.getMessages());
// }
// }
//
private String toString(DataType val, boolean left) throws IOException {
if (val instanceof PrimitiveType)
return "'" + ((PrimitiveType) val).getValueAsString()+"'";
@ -406,9 +567,6 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
right = stripLinks(right);
if (left.equalsIgnoreCase(right))
return left;
if (path != null) {
vm(isError ? IssueSeverity.ERROR : IssueSeverity.WARNING, "Elements differ in "+name+": '"+left+"' vs '"+right+"'", path, comp.getMessages(), res.getMessages());
}
return "left: "+left+"; right: "+right;
}

View File

@ -124,18 +124,26 @@ public class ValueSetComparer extends CanonicalResourceComparer {
vs1.setStatus(left.getStatus());
vs1.setDate(new Date());
var ch = compareMetadata(left, right, res.getMetadata(), res);
List<String> chMetadata = new ArrayList<>();
var ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata);
var def = false;
ch = comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch;
if (comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("immutable");
}
if (left.hasCompose() || right.hasCompose()) {
ch = comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch;
if (comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("compose.lockedDate");
}
def = comparePrimitives("compose.inactive", left.getCompose().getInactiveElement(), right.getCompose().getInactiveElement(), res.getMetadata(), IssueSeverity.WARNING, res) || def;
}
res.updatedMetadataState(ch);
res.updatedMetadataState(ch, chMetadata);
def = compareCompose(left.getCompose(), right.getCompose(), res, res.getUnion().getCompose(), res.getIntersection().getCompose()) || def;
res.updateDefinitionsState(def);
compareExpansions(left, right, res);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -149,9 +157,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getInclude().add(l);
res.updateContentState(true);
res.getIncludes().getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed Include", "ValueSet.compose.include")));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "include", l);
}
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "include", l);
} else {
matchR.add(r);
ConceptSetComponent csM = new ConceptSetComponent();
@ -168,9 +174,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getInclude().add(r);
res.updateContentState(true);
res.getIncludes().getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added Include", "ValueSet.compose.include"), r));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -247,6 +251,9 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getValueSet().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed ValueSet", "ValueSet.compose.include.valueSet")));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "valueset", l);
}
} else {
matchVSR.add(r);
if (l.getValue().equals(r.getValue())) {
@ -260,7 +267,11 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getValueSet().add(r);
res.updateContentState(true);
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r, vmI(IssueSeverity.INFORMATION, "Values are different", "ValueSet.compose.include.valueSet"));
combined.getChildren().add(sm);
combined.getChildren().add(sm);
if (session.isAnnotate()) {
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
}
}
}
@ -268,7 +279,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (!matchVSR.contains(r)) {
union.getValueSet().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Add ValueSet", "ValueSet.compose.include.valueSet"), r));
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Add ValueSet", "ValueSet.compose.include.valueSet"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -279,6 +291,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getConcept().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this Concept", "ValueSet.compose.include.concept")));
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "concept", l);
} else {
matchCR.add(r);
if (l.getCode().equals(r.getCode())) {
@ -297,6 +310,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
combined.getChildren().add(sm);
res.updateContentState(true);
compareConcepts(l, r, sm, null, null);
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
}
}
@ -304,7 +318,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (!matchCR.contains(r)) {
union.getConcept().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this Concept", "ValueSet.compose.include.concept"), r));
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this Concept", "ValueSet.compose.include.concept"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -315,6 +330,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getFilter().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", "ValueSet.compose.include.filter")));
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "filter", l);
} else {
matchFR.add(r);
if (l.getProperty().equals(r.getProperty()) && l.getOp().equals(r.getOp())) {
@ -325,7 +341,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r);
combined.getChildren().add(sm);
if (!compareFilters(l, r, sm, cu, ci)) {
res.updateContentState(true);
res.updateContentState(true);
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
} else {
union.getFilter().add(l);
@ -341,7 +358,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (!matchFR.contains(r)) {
union.getFilter().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this item", "ValueSet.compose.include.filter"), r));
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this item", "ValueSet.compose.include.filter"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;

View File

@ -1,69 +1,80 @@
package org.hl7.fhir.r5.comparison;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.CanonicalResourceComparison;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.ChangeAnalysisState;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class VersionComparisonAnnotation {
public enum AnotationType {
Added, Changed, Deleted;
NoChange, Added, Changed, Deleted;
}
public static final String USER_DATA_NAME = "version-annotation";
private AnotationType type;
// private String comment;
// private String link;
private Map<String, List<Base>> deletedChildren;
private String version;
private CanonicalResourceComparison<? extends CanonicalResource> comp;
private VersionComparisonAnnotation(AnotationType type, String version) {
super();
this.type = type;
this.version = version;
}
//
// private VersionComparisonAnnotation(AnotationType type, String comment) {
// super();
// this.type = type;
// this.comment = comment;
// }
// private VersionComparisonAnnotation(AnotationType type, String comment, String link) {
// super();
// this.type = type;
// this.comment = comment;
// this.link = link;
// }
//
public static void annotate(Base base, String version, CanonicalResourceComparison<? extends CanonicalResource> comp) {
if (version != null) {
VersionComparisonAnnotation vca = new VersionComparisonAnnotation(comp.noChange() ? AnotationType.NoChange : AnotationType.Added, version);
vca.comp = comp;
base.setUserData(USER_DATA_NAME, vca);
}
}
public static void markAdded(Base focus, String version) {
focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Added, version));
if (version != null) {
focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Added, version));
}
}
public static void markChanged(Base focus, String version) {
if (version != null) {
focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Changed, version));
}
}
public static void markDeleted(Base parent, String version, String name, Base other) {
VersionComparisonAnnotation vca = null;
if (parent.hasUserData(USER_DATA_NAME)) {
vca = (VersionComparisonAnnotation) parent.getUserData(USER_DATA_NAME);
assert vca.type != AnotationType.Added;
} else {
vca = new VersionComparisonAnnotation(AnotationType.Changed, version);
parent.setUserData(USER_DATA_NAME, vca);
if (version != null) {
VersionComparisonAnnotation vca = null;
if (parent.hasUserData(USER_DATA_NAME)) {
vca = (VersionComparisonAnnotation) parent.getUserData(USER_DATA_NAME);
assert vca.type != AnotationType.Added;
} else {
vca = new VersionComparisonAnnotation(AnotationType.Changed, version);
parent.setUserData(USER_DATA_NAME, vca);
}
if (vca.deletedChildren == null) {
vca.deletedChildren = new HashMap<>();
}
if (!vca.deletedChildren.containsKey(name)) {
vca.deletedChildren.put(name, new ArrayList<>());
}
other.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Deleted, version));
vca.deletedChildren.get(name).add(other);
}
if (vca.deletedChildren == null) {
vca.deletedChildren = new HashMap<>();
}
if (!vca.deletedChildren.containsKey(name)) {
vca.deletedChildren.put(name, new ArrayList<>());
}
other.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Deleted, version));
vca.deletedChildren.get(name).add(other);
}
public AnotationType getType() {
@ -73,19 +84,6 @@ public class VersionComparisonAnnotation {
this.type = type;
}
// public String getComment() {
// return comment;
// }
// public void setComment(String comment) {
// this.comment = comment;
// }
// public String getLink() {
// return link;
// }
// public void setLink(String link) {
// this.link = link;
// }
public static XhtmlNode render(Base b, XhtmlNode x) {
if (b.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) b.getUserData(USER_DATA_NAME);
@ -98,20 +96,57 @@ public class VersionComparisonAnnotation {
private XhtmlNode render(XhtmlNode x) {
switch (type) {
case Added:
XhtmlNode span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
span.img("icon-change-add.png", "icon");
span.tx(" Added:");
return x;
XhtmlNode spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null);
XhtmlNode spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-add.png", "icon");
spanInner.tx(" Added:");
return spanOuter;
case Changed:
span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been changed since version "+version);
span.img("icon-change-edit.png", "icon");
span.tx(" Changed:");
return x;
spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null);
spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-edit.png", "icon");
spanInner.tx(" Changed:");
return spanOuter;
case Deleted:
span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been removed since version "+version);
span.img("icon-change-remove.png", "icon");
span.tx(" Removed:");
return x.strikethrough();
spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null);
spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed:");
return spanOuter.strikethrough();
default:
return x;
}
}
public static XhtmlNode renderDiv(Base b, XhtmlNode x) {
if (b.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) b.getUserData(USER_DATA_NAME);
return self.renderDiv(x);
} else {
return x;
}
}
private XhtmlNode renderDiv(XhtmlNode x) {
switch (type) {
case Added:
XhtmlNode divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px");
XhtmlNode spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-add.png", "icon");
spanInner.tx(" Added:");
return divOuter;
case Changed:
divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px");
spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-edit.png", "icon");
spanInner.tx(" Changed:");
return divOuter;
case Deleted:
divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px");
spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed:");
return divOuter.strikethrough();
default:
return x;
}
@ -142,5 +177,14 @@ public class VersionComparisonAnnotation {
}
return result;
}
public static CanonicalResourceComparison<? extends CanonicalResource> artifactComparison(Base base) {
if (base.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME);
return self.comp;
} else {
return null;
}
}
}

View File

@ -1271,7 +1271,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(coding);
if (options.isGuessSystem()) {
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
}
setTerminologyOptions(options, pIn);
return pIn;
@ -1288,7 +1288,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding());
if (options.isGuessSystem()) {
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
}
if (valueSet != null) {
pIn.addParameter().setName("valueSet").setResource(valueSet);
@ -1301,7 +1301,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Parameters pIn = new Parameters();
pIn.addParameter().setName("coding").setValue(codingValidationRequest.getCoding());
if (options.isGuessSystem()) {
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
pIn.addParameter().setName("inferSystem").setValue(new BooleanType(true));
}
if (vsUrl != null) {
pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl));

View File

@ -1344,7 +1344,7 @@ public class Element extends Base {
}
}
throw new Error("Unrecognised name "+name+" on "+this.name);
throw new Error("Unrecognised property '"+name+"' on "+this.name);
}
@Override

View File

@ -9,9 +9,12 @@ import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemHierarchyMeaning;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
@ -29,6 +32,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.LoincLinker;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class CodeSystemRenderer extends TerminologyRenderer {
@ -84,12 +88,12 @@ public class CodeSystemRenderer extends TerminologyRenderer {
tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Value", getContext().getLang()));
for (CodeSystemFilterComponent f : cs.getFilter()) {
tr = tbl.tr();
tr.td().tx(f.getCode());
tr.td().tx(f.getDescription());
VersionComparisonAnnotation.render(f, tr.td()).tx(f.getCode());
VersionComparisonAnnotation.render(f.getDescriptionElement(), tr.td()).tx(f.getDescription());
XhtmlNode td = tr.td();
for (Enumeration<org.hl7.fhir.r5.model.Enumerations.FilterOperator> t : f.getOperator())
td.tx(t.asStringValue()+" ");
tr.td().tx(f.getValue());
VersionComparisonAnnotation.render(t, td).tx(t.asStringValue()+" ");
VersionComparisonAnnotation.render(f.getValueElement(), tr.td()).tx(f.getValue());
}
}
}
@ -125,13 +129,13 @@ public class CodeSystemRenderer extends TerminologyRenderer {
if (hasRendered) {
tr.td().tx(ToolingExtensions.getPresentation(p, p.getCodeElement()));
}
tr.td().tx(p.getCode());
VersionComparisonAnnotation.render(p, tr.td()).tx(p.getCode());
if (hasURI) {
tr.td().tx(p.getUri());
VersionComparisonAnnotation.render(p.getUriElement(), tr.td()).tx(p.getUri());
}
tr.td().tx(p.hasType() ? p.getType().toCode() : "");
VersionComparisonAnnotation.render(p.getTypeElement(), tr.td()).tx(p.hasType() ? p.getType().toCode() : "");
if (hasDescription) {
tr.td().tx(p.getDescription());
VersionComparisonAnnotation.render(p.getDescriptionElement(), tr.td()).tx(p.getDescription());
}
}
return true;
@ -160,8 +164,9 @@ public class CodeSystemRenderer extends TerminologyRenderer {
features = "features"; // ?
}
return context.getContext().formatMessage(I18nConstants.RND_CS_CONTENT_SUPPLEMENT, features);
default:
throw new FHIRException("Unknown CodeSystemContentMode mode");
}
throw new FHIRException("Unknown CodeSystemContentMode mode");
}
private boolean generateCodeSystemContent(XhtmlNode x, CodeSystem cs, boolean hasExtensions, List<UsedConceptMap> maps, boolean props) throws FHIRFormatError, DefinitionException, IOException {
@ -169,7 +174,10 @@ public class CodeSystemRenderer extends TerminologyRenderer {
x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Concepts", getContext().getLang()));
}
XhtmlNode p = x.para();
p.param("cs").code().tx(cs.getUrl());
VersionComparisonAnnotation.render(cs.getUrlElement(), p.param("cs")).code().tx(cs.getUrl());
makeCasedParam(p.param("cased"), cs, cs.getCaseSensitiveElement());
makeHierarchyParam(p.param("h"), cs, cs.getHierarchyMeaningElement());
p.paramValue("code-count", CodeSystemUtilities.countCodes(cs));
p.sentenceForParams(sentenceForContent(cs.getContent(), cs));
if (cs.getContent() == CodeSystemContentMode.NOTPRESENT) {
@ -236,6 +244,30 @@ public class CodeSystemRenderer extends TerminologyRenderer {
return hasExtensions;
}
private void makeHierarchyParam(XhtmlNode x, CodeSystem cs, Enumeration<CodeSystemHierarchyMeaning> hm) {
if (hm.hasValue()) {
String s = hm.getValue().getDisplay();
VersionComparisonAnnotation.render(hm, x).tx(" in a "+s+" heirarchy");
} else if (VersionComparisonAnnotation.hasDeleted(cs, "hierarchyMeaning")) {
makeHierarchyParam(x, null, (Enumeration<CodeSystemHierarchyMeaning>) VersionComparisonAnnotation.getDeleted(cs, "hierarchyMeaning").get(0));
} else if (CodeSystemUtilities.hasHierarchy(cs)) {
x.tx(" in an undefined heirarchy");
} else {
x.tx("");
}
}
private void makeCasedParam(XhtmlNode x, CodeSystem cs, BooleanType caseSensitiveElement) {
if (caseSensitiveElement.hasValue()) {
String s = caseSensitiveElement.getValue() == true? "case-sensitive" : "case-insensitive";
VersionComparisonAnnotation.render(caseSensitiveElement, x).tx(s);
} else if (VersionComparisonAnnotation.hasDeleted(cs, "caseSensitive")) {
makeCasedParam(x, null, (BooleanType) VersionComparisonAnnotation.getDeleted(cs, "caseSensitive").get(0));
} else {
x.tx("");
}
}
private void listConceptLanguages(CodeSystem cs, ConceptDefinitionComponent c, List<String> langs) {
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
if (cd.hasLanguage() && !langs.contains(cd.getLanguage()) && (!cs.hasLanguage() || !cs.getLanguage().equals(cd.getLanguage()))) {
@ -377,7 +409,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
if (link != null) {
td.ah(link).attribute("style", "white-space:nowrap").addText(c.getCode());
} else {
td.attribute("style", "white-space:nowrap").addText(c.getCode());
VersionComparisonAnnotation.render(c, td.attribute("style", "white-space:nowrap")).addText(c.getCode());
}
XhtmlNode a;
if (c.hasCodeElement()) {
@ -390,13 +422,13 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
if (hasDefinitions) {
td = tr.td();
if (c != null &&
c.hasDefinitionElement()) {
if (c != null &&c.hasDefinitionElement()) {
if (getContext().getLang() == null) {
if (hasMarkdownInDefinitions(cs))
addMarkdown(td, c.getDefinition());
else
td.addText(c.getDefinition());
if (hasMarkdownInDefinitions(cs)) {
addMarkdown(VersionComparisonAnnotation.renderDiv(c.getDefinitionElement(), td), c.getDefinition());
} else {
VersionComparisonAnnotation.render(c.getDefinitionElement(), td).addText(c.getDefinition());
}
} else if (getContext().getLang().equals("*")) {
boolean sl = false;
for (ConceptDefinitionDesignationComponent cd : c.getDesignation())
@ -404,9 +436,9 @@ public class CodeSystemRenderer extends TerminologyRenderer {
sl = true;
td.addText((sl ? cs.getLanguage("en")+": " : ""));
if (hasMarkdownInDefinitions(cs))
addMarkdown(td, c.getDefinition());
addMarkdown(VersionComparisonAnnotation.renderDiv(c.getDefinitionElement(), td), c.getDefinition());
else
td.addText(c.getDefinition());
VersionComparisonAnnotation.render(c.getDefinitionElement(), td).addText(c.getDefinition());
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
td.br();
@ -414,7 +446,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
}
} else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) {
td.addText(c.getDefinition());
VersionComparisonAnnotation.render(c.getDefinitionElement(), td).addText(c.getDefinition());
} else {
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) {
@ -591,7 +623,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
public void renderDisplayName(ConceptDefinitionComponent c, CodeSystem cs, XhtmlNode td) {
if (c.hasDisplayElement()) {
if (getContext().getLang() == null) {
td.addText(c.getDisplay());
VersionComparisonAnnotation.render(c.getDisplayElement(), td).addText(c.getDisplay());
} else if (getContext().getLang().equals("*")) {
boolean sl = false;
for (ConceptDefinitionDesignationComponent cd : c.getDesignation())
@ -605,7 +637,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
}
} else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) {
td.addText(c.getDisplay());
VersionComparisonAnnotation.render(c.getDisplayElement(), td).addText(c.getDisplay());
} else {
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) {

View File

@ -2727,30 +2727,24 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return ed.getPath().substring(ed.getPath().indexOf(".")+1);
}
public static String formatTypeSpecifiers(IWorkerContext context, ElementDefinition d) {
StringBuilder b = new StringBuilder();
public XhtmlNode formatTypeSpecifiers(IWorkerContext context, ElementDefinition d) {
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
boolean first = true;
for (Extension e : d.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) {
if (first) first = false; else b.append("<br/>");
if (first) first = false; else x.br();
String cond = ToolingExtensions.readStringExtension(e, "condition");
String type = ToolingExtensions.readStringExtension(e, "type");
b.append("If <code>");
b.append(Utilities.escapeXml(cond));
b.append("</code> then the type is ");
x.tx("If ");
x.code().tx(cond);
x.tx(" then the type is ");
StructureDefinition sd = context.fetchTypeDefinition(type);
if (sd == null) {
b.append("<code>");
b.append(Utilities.escapeXml(type));
b.append("</code>");
x.code().tx(type);
} else {
b.append("<a href=\"");
b.append(sd.getWebPath());
b.append("\">");
b.append(Utilities.escapeXml(sd.getTypeName()));
b.append("</a>");
x.ah(sd.getWebPath()).tx(sd.getTypeName());
}
}
return b.toString();
return first ? null : x;
}
public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set<String> outputTracker, RenderingContext rc) throws IOException, FHIRException {

View File

@ -1234,33 +1234,34 @@ public class ValueSetRenderer extends TerminologyRenderer {
li.tx(", ");
}
}
XhtmlNode wli = VersionComparisonAnnotation.render(f, li);
if (f.getOp() == FilterOperator.EXISTS) {
if (f.getValue().equals("true")) {
li.tx(f.getProperty()+" exists");
wli.tx(f.getProperty()+" exists");
} else {
li.tx(f.getProperty()+" doesn't exist");
wli.tx(f.getProperty()+" doesn't exist");
}
} else {
li.tx(f.getProperty()+" "+describe(f.getOp())+" ");
wli.tx(f.getProperty()+" "+describe(f.getOp())+" ");
if (e != null && codeExistsInValueSet(e, f.getValue())) {
String href = getContext().fixReference(getCsRef(e));
if (href.contains("#"))
href = href + "-"+Utilities.nmtokenize(f.getValue());
else
href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue());
li.ah(href).addText(f.getValue());
wli.ah(href).addText(f.getValue());
} else if ("concept".equals(f.getProperty()) && inc.hasSystem()) {
li.addText(f.getValue());
wli.addText(f.getValue());
ValidationResult vr = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions(), inc.getSystem(), inc.getVersion(), f.getValue(), null);
if (vr.isOk()) {
li.tx(" ("+vr.getDisplay()+")");
wli.tx(" ("+vr.getDisplay()+")");
}
}
else
li.addText(f.getValue());
wli.addText(f.getValue());
String disp = ToolingExtensions.getDisplayHint(f);
if (disp != null)
li.tx(" ("+disp+")");
wli.tx(" ("+disp+")");
}
}
}
@ -1273,7 +1274,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
first = false;
else
li.tx(", ");
AddVsRef(vs.asStringValue(), li, vsRes);
XhtmlNode wli = VersionComparisonAnnotation.render(vs, li);
AddVsRef(vs.asStringValue(), wli, vsRes);
}
}
if (inc.hasExtension(ToolingExtensions.EXT_EXPAND_RULES) || inc.hasExtension(ToolingExtensions.EXT_EXPAND_GROUP)) {
@ -1289,12 +1291,14 @@ public class ValueSetRenderer extends TerminologyRenderer {
first = false;
else
li.tx(", ");
AddVsRef(vs.asStringValue(), li, vsRes);
XhtmlNode wli = VersionComparisonAnnotation.render(vs, li);
AddVsRef(vs.asStringValue(), wli, vsRes);
}
} else {
XhtmlNode xul = li.ul();
for (UriType vs : inc.getValueSet()) {
AddVsRef(vs.asStringValue(), xul.li(), vsRes);
XhtmlNode wli = VersionComparisonAnnotation.render(vs, xul.li());
AddVsRef(vs.asStringValue(), wli, vsRes);
}
}

View File

@ -244,10 +244,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
if (result == null) {
msg = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.WARNING, unknownSystems.isEmpty() ? IssueType.INVALID : IssueType.NOTFOUND, path, msg));
info.getIssues().addAll(makeIssue(IssueSeverity.WARNING, unknownSystems.isEmpty() ? IssueType.CODEINVALID : IssueType.NOTFOUND, path, msg));
} else if (!result) {
msg = context.formatMessagePlural(code.getCoding().size(), I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, unknownSystems.isEmpty() ? IssueType.INVALID : IssueType.NOTFOUND, path, msg));
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, unknownSystems.isEmpty() ? IssueType.CODEINVALID : IssueType.NOTFOUND, path, msg));
}
}
if (info.hasErrors()) {
@ -488,7 +488,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
// {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), code.toString());
res.addToMessage(msg).setSeverity(IssueSeverity.ERROR);
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path, msg));
res.setDefinition(null);
res.setSystem(null);
res.setDisplay(null);
@ -507,7 +507,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
} else if ((res != null && !res.isOk())) {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), code.toString());
res.setMessage(res.getMessage()+"; "+msg);
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path, msg));
}
}
if (res != null && res.getSeverity() == IssueSeverity.INFORMATION) {
@ -638,10 +638,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (cc == null) {
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
String msg = context.formatMessage(I18nConstants.UNKNOWN_CODE__IN_FRAGMENT, code.getCode(), cs.getUrl());
return new ValidationResult(IssueSeverity.WARNING, msg, makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path+".code", msg));
return new ValidationResult(IssueSeverity.WARNING, msg, makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path+".code", msg));
} else {
String msg = context.formatMessage(I18nConstants.UNKNOWN_CODE__IN_, code.getCode(), cs.getUrl());
return new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path+".code", msg));
return new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path+".code", msg));
}
}
Coding vc = new Coding().setCode(cc.getCode()).setSystem(cs.getUrl()).setVersion(cs.getVersion()).setDisplay(getPreferredDisplay(cc, cs));
@ -698,10 +698,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
if (b.count() == 0) {
String msg = context.formatMessagePlural(options.getLanguages().size(), I18nConstants.NO_VALID_DISPLAY_FOUND, code.getSystem(), code.getCode(), code.getDisplay(), options.langSummary());
return new ValidationResult(IssueSeverity.WARNING, msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(IssueSeverity.WARNING, IssueType.INVALID, path+".display", msg));
return new ValidationResult(IssueSeverity.WARNING, msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(IssueSeverity.WARNING, IssueType.CODEINVALID, path+".display", msg));
} else {
String msg = context.formatMessagePlural(b.count(), ws ? I18nConstants.DISPLAY_NAME_WS_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF : I18nConstants.DISPLAY_NAME_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF, code.getSystem(), code.getCode(), b.toString(), code.getDisplay(), options.langSummary());
return new ValidationResult(dispWarningStatus(), msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(dispWarning(), IssueType.INVALID, path+".display", msg));
return new ValidationResult(dispWarningStatus(), msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(dispWarning(), IssueType.CODEINVALID, path+".display", msg));
}
}

View File

@ -316,7 +316,7 @@ public class CompareUtilities extends BaseTestingUtilities {
if (i == expectedArray.size() - 1 && isOptional(expectedArray.get(i))) {
return null; // this is OK
} else {
return "One or more array items did not match at "+path;
return "One or more array items did not match at "+path+" starting at index "+i;
}
}
String s = compareNodes(path + "[" + Integer.toString(i) + "]", expectedArray.get(i), actualArray.get(c));

View File

@ -54,21 +54,24 @@ public class DefinitionNavigator {
private List<String> names = new ArrayList<String>();
private TypeRefComponent typeOfChildren;
private String path;
private boolean diff;
public DefinitionNavigator(IWorkerContext context, StructureDefinition structure) throws DefinitionException {
if (!structure.hasSnapshot())
public DefinitionNavigator(IWorkerContext context, StructureDefinition structure, boolean diff) throws DefinitionException {
if (!diff && !structure.hasSnapshot())
throw new DefinitionException("Snapshot required");
this.context = context;
this.structure = structure;
this.index = 0;
this.diff = diff;
this.path = current().getPath();
names.add(nameTail());
}
private DefinitionNavigator(IWorkerContext context, StructureDefinition structure, int index, String path, List<String> names, String type) {
private DefinitionNavigator(IWorkerContext context, StructureDefinition structure, boolean diff, int index, String path, List<String> names, String type) {
this.path = path;
this.context = context;
this.structure = structure;
this.diff = diff;
this.index = index;
if (type == null)
for (String name : names)
@ -96,8 +99,16 @@ public class DefinitionNavigator {
public List<String> getNames() {
return names;
}
private List<ElementDefinition> list() {
if (diff) {
return structure.getDifferential().getElement();
} else {
return structure.getSnapshot().getElement();
}
}
public ElementDefinition current() {
return structure.getSnapshot().getElement().get(index);
return list().get(index);
}
public List<DefinitionNavigator> slices() throws DefinitionException {
@ -119,14 +130,15 @@ public class DefinitionNavigator {
String prefix = current().getPath()+".";
Map<String, DefinitionNavigator> nameMap = new HashMap<String, DefinitionNavigator>();
for (int i = index + 1; i < structure.getSnapshot().getElement().size(); i++) {
String path = structure.getSnapshot().getElement().get(i).getPath();
for (int i = index + 1; i < list().size(); i++) {
String path = list().get(i).getPath();
if (path.startsWith(prefix) && !path.substring(prefix.length()).contains(".")) {
DefinitionNavigator dn = new DefinitionNavigator(context, structure, i, this.path+"."+tail(path), names, null);
DefinitionNavigator dn = new DefinitionNavigator(context, structure, diff, i, this.path+"."+tail(path), names, null);
if (nameMap.containsKey(path)) {
DefinitionNavigator master = nameMap.get(path);
if (!master.current().hasSlicing())
ElementDefinition cm = master.current();
if (!cm.hasSlicing())
throw new DefinitionException("Found slices with no slicing details at "+dn.current().getPath());
if (master.slices == null)
master.slices = new ArrayList<DefinitionNavigator>();
@ -180,7 +192,7 @@ public class DefinitionNavigator {
typeOfChildren = null;
StructureDefinition sd = context.fetchResource(StructureDefinition.class, /* GF#13465 : this somehow needs to be revisited type.hasProfile() ? type.getProfile() : */ type.getWorkingCode(), src);
if (sd != null) {
DefinitionNavigator dn = new DefinitionNavigator(context, sd, 0, path, names, sd.getType());
DefinitionNavigator dn = new DefinitionNavigator(context, sd, diff, 0, path, names, sd.getType());
typeChildren = dn.children();
} else
throw new DefinitionException("Unable to find definition for "+type.getWorkingCode()+(type.hasProfile() ? "("+type.getProfile()+")" : ""));

View File

@ -965,6 +965,7 @@ public class I18nConstants {
public static final String MSG_DEPENDS_ON_DRAFT = "MSG_DEPENDS_ON_DRAFT";
public static final String MSG_DEPENDS_ON_EXTENSION = "MSG_DEPENDS_ON_EXTENSION";
public static final String MSG_DEPENDS_ON_PROFILE = "MSG_DEPENDS_ON_PROFILE";
public static final String VALIDATION_VAL_STATUS_INCONSISTENT = "VALIDATION_VAL_STATUS_INCONSISTENT";
}

View File

@ -6,6 +6,6 @@ public class CommonPackages {
public static final String VER_XVER = "0.0.12";
public static final String ID_PUBPACK = "hl7.fhir.pubpack";
public static final String VER_PUBPACK = "0.1.4";
public static final String VER_PUBPACK = "0.1.5";
}

View File

@ -371,9 +371,13 @@ public class XhtmlComposer {
}
}
public String compose(XhtmlNodeList childNodes) {
// TODO Auto-generated method stub
return null;
public String compose(XhtmlNodeList nodes) throws IOException {
StringWriter sdst = new StringWriter();
dst = sdst;
for (XhtmlNode node : nodes) {
writeNode("", node, false);
}
return sdst.toString();
}
}

View File

@ -65,6 +65,13 @@ public abstract class XhtmlFluent {
return addTag("div");
}
public XhtmlNode div(String style) {
XhtmlNode x = addTag("div");
if (!Utilities.noString(style))
x.attribute("style", style);
return x;
}
public XhtmlNode para() {
return addTag("p");
}
@ -110,7 +117,15 @@ public abstract class XhtmlFluent {
}
public XhtmlNode ah(String href) {
return addTag("a").attribute("href", href);
if (href == null) {
if (this instanceof XhtmlNode) {
return (XhtmlNode) this;
} else {
return addTag("span");
}
} else {
return addTag("a").attribute("href", href);
}
}
public XhtmlNode ah(String href, String title) {
@ -121,6 +136,17 @@ public abstract class XhtmlFluent {
return x;
}
public XhtmlNode ahWithText(String preText, String href, String title, String text, String postText) {
tx(preText);
XhtmlNode x = addTag("a").attribute("href", href);
if (title != null) {
x.attribute("title", title);
}
x.tx(text);
tx(postText);
return x;
}
/**
* make it a code if it's not a link
* @param href
@ -168,7 +194,32 @@ public abstract class XhtmlFluent {
return res;
}
public XhtmlNode span(String style) {
XhtmlNode res = addTag("span");
if (!Utilities.noString(style))
res.attribute("style", style);
return res;
}
public XhtmlNode span() {
return addTag("span");
}
public XhtmlNode spanClss(String clssName) {
XhtmlNode res = addTag("span");
if (!Utilities.noString(clssName))
res.attribute("class", clssName);
return res;
}
public void codeWithText(String preText, String text, String postText) {
tx(preText);
XhtmlNode code = addTag("code");
code.tx(text);
tx(postText);
}
public XhtmlNode code(String text) {
XhtmlNode code = addTag("code");
code.tx(text);

View File

@ -872,4 +872,24 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
return addTag("s");
}
public XhtmlNode svg() {
return addTag("svg");
}
public XhtmlNode path(String value) {
return addTag("path").attribute("d", value);
}
public void copyAllContent(XhtmlNode other) {
getChildNodes().addAll(other.getChildNodes());
getAttributes().putAll(other.getAttributes());
if (!Utilities.noString(other.getContent())) {
tx(other.getContent());
}
}
}

View File

@ -947,10 +947,10 @@ SD_OBGLIGATION_INHERITS_PROFILE_NO_TARGET = Unable to read a value from this ext
SD_OBGLIGATION_INHERITS_PROFILE_TARGET_NOT_FOUND = The profile ''{0}'' could not be found
SD_OBGLIGATION_INHERITS_PROFILE_NOT_RIGHT_TYPE = The profile ''{0}'' is not marked as an obligation profile
SD_OBGLIGATION_INHERITS_PROFILE_NOT_RIGHT_BASE = The profile ''{0}'' has a different base ''{1}'' from that expected ''{2}''
RND_CS_CONTENT_COMPLETE = This code system <param name="cs"/> defines the following code<if test="code-count != 1">s</if>:
RND_CS_CONTENT_EXAMPLE = This code system <param name="cs"/> provides some example code<if test="code-count != 1">s</if>:
RND_CS_CONTENT_FRAGMENT = This code system <param name="cs"/> provides a fragment that includes following code<if test="code-count != 1">s</if>:
RND_CS_CONTENT_NOTPRESENT = This code system <param name="cs"/> defines codes, but no codes are represented here
RND_CS_CONTENT_COMPLETE = This <param name="cased"/> code system <param name="cs"/> defines the following code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_EXAMPLE = This <param name="cased"/> code system <param name="cs"/> provides some example code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_FRAGMENT = This <param name="cased"/> code system <param name="cs"/> provides a fragment that includes following code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_NOTPRESENT = This <param name="cased"/> code system <param name="cs"/> defines codes<param name="h"/>, but no codes are represented here
RND_CS_CONTENT_SUPPLEMENT = This code system <param name="cs"/> defines {0} on the following code<if test="code-count != 1">s</if>:
QUESTIONNAIRE_Q_UNKNOWN_DERIVATION = The questionnaire ''{0}'' referred to in the derivation could not be found
QUESTIONNAIRE_Q_NO_DERIVATION_TYPE = The questionnaire ''{0}'' has no derivation type specified using the ''http://hl7.org/fhir/StructureDefinition/questionnaire-derivationType'' extension, so derivation has not been checked
@ -1015,7 +1015,8 @@ MSG_RETIRED = Reference to retired item {0}
MSG_EXPERIMENTAL = Reference to experimental item {0}
MSG_DRAFT = Reference to draft item {0}
INACTIVE_CODE_WARNING = The code ''{0}'' is valid but is not active
SD_ED_TYPE_PROFILE_WRONG_TYPE = The type {0} is not in the list of allowed types {1} in the profile {2}
SD_ED_TYPE_PROFILE_WRONG_TYPE_one = The type {0} is not in the list of allowed type {1} in the profile {2}
SD_ED_TYPE_PROFILE_WRONG_TYPE_other = The type {0} is not in the list of allowed types {1} in the profile {2}
MSG_DEPENDS_ON_DEPRECATED = The {0} {1} is deprecated
MSG_DEPENDS_ON_WITHDRAWN = The {0} {1} is withdrawn
MSG_DEPENDS_ON_RETIRED = The {0} {1} is retired
@ -1023,3 +1024,5 @@ MSG_DEPENDS_ON_EXPERIMENTAL = The {0} {1} is an experimental resource
MSG_DEPENDS_ON_DRAFT = The {0} {1} is a draft resource
MSG_DEPENDS_ON_EXTENSION = extension
MSG_DEPENDS_ON_PROFILE = profile
VALIDATION_VAL_STATUS_INCONSISTENT = The resource status ''{0}'' amd the standards status ''{1}'' are not consistent

View File

@ -835,3 +835,6 @@ UNICODE_XML_BAD_CHARS_other =
Display_Name_WS_for__should_be_one_of__instead_of_one =
Display_Name_WS_for__should_be_one_of__instead_of_many =
Display_Name_WS_for__should_be_one_of__instead_of_other =
SD_ED_TYPE_PROFILE_WRONG_TYPE_one =
SD_ED_TYPE_PROFILE_WRONG_TYPE_many =
SD_ED_TYPE_PROFILE_WRONG_TYPE_other =

View File

@ -5139,7 +5139,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
public boolean checkSpecials(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) {
// specific known special validations
if (VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(element.getType())) {
Base base = element.getExtensionValue(ToolingExtensions.EXT_STANDARDS_STATUS);
String standardsStatus = base != null && base.isPrimitive() ? base.primitiveValue() : null;
String status = element.getNamedChildValue("status");
if (!Utilities.noString(status) && !Utilities.noString(standardsStatus)) {
warning(errors, "2023-08-14", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), statusCodesConsistent(status, standardsStatus), I18nConstants.VALIDATION_VAL_STATUS_INCONSISTENT, status, standardsStatus);
}
}
if (element.getType().equals(BUNDLE)) {
return new BundleValidator(this, serverBase).validateBundle(errors, element, stack, checkSpecials, hostContext, pct, mode);
} else if (element.getType().equals("Observation")) {
@ -5171,6 +5178,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private boolean statusCodesConsistent(String status, String standardsStatus) {
switch (standardsStatus) {
case "draft": return Utilities.existsInList(status, "draft");
case "normative": return Utilities.existsInList(status, "active");
case "trial-use": return Utilities.existsInList(status, "draft", "active");
case "informative": return Utilities.existsInList(status, "draft", "active", "retired");
case "deprecated": return Utilities.existsInList(status, "retired");
case "withdrawn": return Utilities.existsInList(status, "retired");
case "external": return Utilities.existsInList(status, "draft", "active", "retired");
}
return true;
}
private ResourceValidationTracker getResourceTracker(Element element) {
ResourceValidationTracker res = resourceTracker.get(element);
if (res == null) {

View File

@ -181,7 +181,7 @@ public class TerminologyServiceTests {
e.setCode(IssueType.BUSINESSRULE);
break;
case CODESYSTEM_UNSUPPORTED:
e.setCode(IssueType.INVALID);
e.setCode(IssueType.CODEINVALID);
break;
case INTERNAL_ERROR:
e.setCode(IssueType.EXCEPTION);

View File

@ -158,3 +158,19 @@ v: {
"system" : "urn:ietf:bcp:13"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
"code" : "001"
}, "url": "http://hl7.org/fhir/ValueSet/jurisdiction", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "World",
"code" : "001",
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
}
-------------------------------------------------------------------------------------