001package org.hl7.fhir.r5.comparison; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Date; 007import java.util.List; 008import java.util.Set; 009 010import org.hl7.fhir.exceptions.DefinitionException; 011import org.hl7.fhir.exceptions.FHIRException; 012import org.hl7.fhir.exceptions.FHIRFormatError; 013import org.hl7.fhir.r5.comparison.ValueSetComparer.ValueSetComparison; 014import org.hl7.fhir.r5.conformance.profile.BindingResolution; 015import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; 016import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 017import org.hl7.fhir.r5.context.IWorkerContext; 018import org.hl7.fhir.r5.formats.IParser; 019import org.hl7.fhir.r5.formats.JsonParser; 020import org.hl7.fhir.r5.model.Base; 021import org.hl7.fhir.r5.model.Coding; 022import org.hl7.fhir.r5.model.DataType; 023import org.hl7.fhir.r5.model.ElementDefinition; 024import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; 025import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; 026import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; 027import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; 028import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 029import org.hl7.fhir.r5.model.Enumeration; 030import org.hl7.fhir.r5.model.Enumerations.BindingStrength; 031import org.hl7.fhir.r5.model.IntegerType; 032import org.hl7.fhir.r5.model.PrimitiveType; 033import org.hl7.fhir.r5.model.Resource; 034import org.hl7.fhir.r5.model.StringType; 035import org.hl7.fhir.r5.model.StructureDefinition; 036import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 037import org.hl7.fhir.r5.model.ValueSet; 038import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus; 039import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer; 040import org.hl7.fhir.r5.renderers.utils.RenderingContext; 041import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 042import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; 043import org.hl7.fhir.r5.utils.DefinitionNavigator; 044import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 045import org.hl7.fhir.utilities.Utilities; 046import org.hl7.fhir.utilities.validation.ValidationMessage; 047import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 048import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 049import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 050import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 051import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableGenerationMode; 052import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 053import org.hl7.fhir.utilities.xhtml.XhtmlNode; 054 055import kotlin.NotImplementedError; 056 057public class StructureDefinitionComparer extends CanonicalResourceComparer implements ProfileKnowledgeProvider { 058 059 public class ProfileComparison extends CanonicalResourceComparison<StructureDefinition> { 060 061 private StructuralMatch<ElementDefinitionNode> combined; 062 063 public ProfileComparison(StructureDefinition left, StructureDefinition right) { 064 super(left, right); 065 combined = new StructuralMatch<ElementDefinitionNode>(); // base 066 } 067 068 public StructuralMatch<ElementDefinitionNode> getCombined() { 069 return combined; 070 } 071 072 @Override 073 protected String abbreviation() { 074 return "sd"; 075 } 076 077 @Override 078 protected String summary() { 079 return "Profile: "+left.present()+" vs "+right.present(); 080 } 081 082 @Override 083 protected String fhirType() { 084 return "StructureDefinition"; 085 } 086 @Override 087 protected void countMessages(MessageCounts cnts) { 088 super.countMessages(cnts); 089 combined.countMessages(cnts); 090 } 091 092 } 093 094 095 private class ElementDefinitionNode { 096 private ElementDefinition def; 097 private StructureDefinition src; 098 private ElementDefinitionNode(StructureDefinition src, ElementDefinition def) { 099 super(); 100 this.src = src; 101 this.def = def; 102 } 103 public ElementDefinition getDef() { 104 return def; 105 } 106 public StructureDefinition getSrc() { 107 return src; 108 } 109 } 110 111 private ProfileUtilities utilsLeft; 112 private ProfileUtilities utilsRight; 113 114 public StructureDefinitionComparer(ComparisonSession session, ProfileUtilities utilsLeft, ProfileUtilities utilsRight) { 115 super(session); 116 this.utilsLeft = utilsLeft; 117 this.utilsRight = utilsRight; 118 } 119 120 @Override 121 protected String fhirType() { 122 return "StructureDefinition"; 123 } 124 125 public ProfileComparison compare(StructureDefinition left, StructureDefinition right) throws DefinitionException, FHIRFormatError, IOException { 126 check(left, "left"); 127 check(right, "right"); 128 129 ProfileComparison res = new ProfileComparison(left, right); 130 session.identify(res); 131 StructureDefinition sd = new StructureDefinition(); 132 res.setUnion(sd); 133 session.identify(sd); 134 sd.setName("Union"+left.getName()+"And"+right.getName()); 135 sd.setTitle("Union of "+left.getTitle()+" And "+right.getTitle()); 136 sd.setStatus(left.getStatus()); 137 sd.setDate(new Date()); 138 139 StructureDefinition sd1 = new StructureDefinition(); 140 res.setIntersection(sd1); 141 session.identify(sd1); 142 sd1.setName("Intersection"+left.getName()+"And"+right.getName()); 143 sd1.setTitle("Intersection of "+left.getTitle()+" And "+right.getTitle()); 144 sd1.setStatus(left.getStatus()); 145 sd1.setDate(new Date()); 146 147 List<String> chMetadata = new ArrayList<>(); 148 boolean ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata, right); 149 if (comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.WARNING, res)) { 150 ch = true; 151 chMetadata.add("fhirVersion"); 152 } 153 if (comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.WARNING, res)) { 154 ch = true; 155 chMetadata.add("kind"); 156 } 157 if (comparePrimitives("abstract", left.getAbstractElement(), right.getAbstractElement(), res.getMetadata(), IssueSeverity.WARNING, res)) { 158 ch = true; 159 chMetadata.add("abstract"); 160 } 161 res.updatedMetadataState(ch, chMetadata); 162 163 ch = false; 164 ch = comparePrimitives("type", left.getTypeElement(), right.getTypeElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch; 165 ch = comparePrimitives("baseDefinition", left.getBaseDefinitionElement(), right.getBaseDefinitionElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch; 166 if (left.getType().equals(right.getType())) { 167 DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left, false); 168 DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right, false); 169 StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(left, ln.current()), new ElementDefinitionNode(right, rn.current())); 170 compareElements(res, sm, ln.path(), null, ln, rn); 171 res.combined = sm; 172 ln = new DefinitionNavigator(session.getContextLeft(), left, true); 173 rn = new DefinitionNavigator(session.getContextRight(), right, true); 174 ch = compareDiff(ln.path(), null, ln, rn, res, right) || ch; 175 // we don't preserve the differences - we only want the annotations 176 } 177 res.updateDefinitionsState(ch); 178 179 session.annotate(right, res); 180 return res; 181 } 182 183 private void check(StructureDefinition sd, String name) { 184 if (sd == null) 185 throw new DefinitionException("No StructureDefinition provided ("+name+": "+sd.getName()+")"); 186// if (sd.getType().equals("Extension")) { 187// throw new DefinitionException("StructureDefinition is for an extension - use ExtensionComparer instead ("+name+": "+sd.getName()+")"); 188// } 189 if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { 190 throw new DefinitionException("StructureDefinition is not for an profile - can't be compared ("+name+": "+sd.getName()+")"); 191 } 192 if (sd.getSnapshot().getElement().isEmpty()) 193 throw new DefinitionException("StructureDefinition snapshot is empty ("+name+": "+sd.getName()+")"); 194 } 195 196 private boolean compareElements(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException { 197 assert(path != null); 198 assert(left != null); 199 assert(right != null); 200 assert(left.path().equals(right.path())); 201 202 boolean def = false; 203 204 if (session.isDebug()) { 205 System.out.println("Compare elements at "+path); 206 } 207 208 // not allowed to be different: 209// ruleEqual(comp, res, left.current().getDefaultValue(), right.current().getDefaultValue(), "defaultValue", path); 210// ruleEqual(comp, res, left.current().getMeaningWhenMissingElement(), right.current().getMeaningWhenMissingElement(), "meaningWhenMissing", path); 211// ruleEqual(comp, res, left.current().getIsModifierElement(), right.current().getIsModifierElement(), "isModifier", path); - this check belongs in the core 212// ruleEqual(comp, res, left.current().getIsSummaryElement(), right.current().getIsSummaryElement(), "isSummary", path); - so does this 213 214 // we ignore slicing right now - we're going to clone the root one anyway, and then think about clones 215 // simple stuff 216 ElementDefinition subset = new ElementDefinition(); 217 subset.setPath(left.path()); 218 if (sliceName != null) 219 subset.setSliceName(sliceName); 220 221 subset.getRepresentation().addAll(left.current().getRepresentation()); // can't be bothered even testing this one 222 subset.setDefaultValue(left.current().getDefaultValue()); 223 subset.setMeaningWhenMissing(left.current().getMeaningWhenMissing()); 224 subset.setIsModifier(left.current().getIsModifier()); 225 subset.setIsSummary(left.current().getIsSummary()); 226 227 // descriptive properties from ElementDefinition - merge them: 228 subset.setLabel(mergeText(comp, res, path, "label", left.current().getLabel(), right.current().getLabel(), false)); 229 comparePrimitivesWithTracking("label", left.current().getLabelElement(), right.current().getLabelElement(), null, IssueSeverity.INFORMATION, comp, right.current()); 230 231 subset.setShort(mergeText(comp, res, path, "short", left.current().getShort(), right.current().getShort(), false)); 232 def = comparePrimitivesWithTracking("short", left.current().getShortElement(), right.current().getShortElement(), null, IssueSeverity.INFORMATION, comp, right.current()) || def; 233 234 subset.setDefinition(mergeText(comp, res, path, "definition", left.current().getDefinition(), right.current().getDefinition(), false)); 235 def = comparePrimitivesWithTracking("definition", left.current().getDefinitionElement(), right.current().getDefinitionElement(), null, IssueSeverity.INFORMATION, comp, right.current()) || def; 236 237 subset.setComment(mergeText(comp, res, path, "comments", left.current().getComment(), right.current().getComment(), false)); 238 def = comparePrimitivesWithTracking("comment", left.current().getCommentElement(), right.current().getCommentElement(), null, IssueSeverity.INFORMATION, comp, right.current()) || def; 239 240 subset.setRequirements(mergeText(comp, res, path, "requirements", left.current().getRequirements(), right.current().getRequirements(), false)); 241 def = comparePrimitivesWithTracking("requirements", left.current().getRequirementsElement(), right.current().getRequirementsElement(), null, IssueSeverity.INFORMATION, comp, right.current()) || def; 242 243 subset.getCode().addAll(mergeCodings(left.current().getCode(), right.current().getCode())); 244 subset.getAlias().addAll(mergeStrings(left.current().getAlias(), right.current().getAlias())); 245 subset.getMapping().addAll(mergeMappings(left.current().getMapping(), right.current().getMapping())); 246 // left will win for example 247 subset.setExample(left.current().hasExample() ? left.current().getExample() : right.current().getExample()); 248 249 if (left.current().getMustSupport() != right.current().getMustSupport()) { 250 vm(IssueSeverity.WARNING, "Elements differ in definition for mustSupport: '"+left.current().getMustSupport()+"' vs '"+right.current().getMustSupport()+"'", path, comp.getMessages(), res.getMessages()); 251 252 } 253 subset.setMustSupport(left.current().getMustSupport() || right.current().getMustSupport()); 254 def = comparePrimitivesWithTracking("mustSupport", left.current().getMustSupportElement(), right.current().getMustSupportElement(), null, IssueSeverity.INFORMATION, null, right.current()) || def; 255 256 ElementDefinition superset = subset.copy(); 257 258 def = comparePrimitivesWithTracking("min", left.current().getMinElement(), right.current().getMinElement(), null, IssueSeverity.INFORMATION, null, right.current()) || def; 259 def = comparePrimitivesWithTracking("max", left.current().getMaxElement(), right.current().getMaxElement(), null, IssueSeverity.INFORMATION, null, right.current()) || def; 260 261 // compare and intersect 262 int leftMin = left.current().getMin(); 263 int rightMin = right.current().getMin(); 264 int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Utilities.parseInt(left.current().getMax(), -1); 265 int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Utilities.parseInt(right.current().getMax(), -1); 266 267 checkMinMax(comp, res, path, leftMin, rightMin, leftMax, rightMax); 268 superset.setMin(unionMin(leftMin, rightMin)); 269 superset.setMax(unionMax(leftMax, rightMax, left.current().getMax(), right.current().getMax())); 270 subset.setMin(intersectMin(leftMin, rightMin)); 271 subset.setMax(intersectMax(leftMax, rightMax, left.current().getMax(), right.current().getMax())); 272 273 superset.getType().addAll(unionTypes(comp, res, path, left.current().getType(), right.current().getType(), left.getStructure(), right.getStructure())); 274 subset.getType().addAll(intersectTypes(comp, res, subset, path, left.current().getType(), right.current().getType())); 275 rule(comp, res, !subset.getType().isEmpty() || (!left.current().hasType() && !right.current().hasType()), path, "Type Mismatch: "+typeCode(left)+" vs "+typeCode(right)); 276 // <fixed[x]><!-- ?? 0..1 * Value must be exactly this --></fixed[x]> 277 // <pattern[x]><!-- ?? 0..1 * Value must have at least these property values --></pattern[x]> 278 superset.setMaxLengthElement(unionMaxLength(left.current().getMaxLength(), right.current().getMaxLength())); 279 subset.setMaxLengthElement(intersectMaxLength(left.current().getMaxLength(), right.current().getMaxLength())); 280 if (left.current().hasBinding() || right.current().hasBinding()) { 281 compareBindings(comp, res, subset, superset, path, left.current(), right.current(), left.getStructure(), right.getStructure()); 282 } 283 // note these are backwards 284 superset.getConstraint().addAll(intersectConstraints(path, left.current().getConstraint(), right.current().getConstraint())); 285 subset.getConstraint().addAll(unionConstraints(comp, res, path, left.current().getConstraint(), right.current().getConstraint())); 286 comp.getIntersection().getSnapshot().getElement().add(subset); 287 comp.getUnion().getSnapshot().getElement().add(superset); 288 289 // add the children 290 def = compareChildren(comp, res, path, left, right) || def; 291// 292// // now process the slices 293// if (left.current().hasSlicing() || right.current().hasSlicing()) { 294// assert sliceName == null; 295// if (isExtension(left.path())) 296// return compareExtensions(outcome, path, superset, subset, left, right); 297// // return true; 298// else { 299// ElementDefinitionSlicingComponent slicingL = left.current().getSlicing(); 300// ElementDefinitionSlicingComponent slicingR = right.current().getSlicing(); 301// // 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. 302// if (left.current().hasSlicing() && !right.current().hasSlicing()) { 303// // 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) 304// // the minimum set is the slicing specified in the slicer 305// subset.setSlicing(slicingL); 306// // stick everything from the right to do with the slices to the subset 307// copySlices(outcome.subset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), left.slices()); 308// } else if (!left.current().hasSlicing() && right.current().hasSlicing()) { 309// // 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) 310// // the minimum set is the slicing specified in the slicer 311// subset.setSlicing(slicingR); 312// // stick everything from the right to do with the slices to the subset 313// copySlices(outcome.subset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), right.slices()); 314// } else if (isTypeSlicing(slicingL) || isTypeSlicing(slicingR)) { 315// superset.getSlicing().setRules(SlicingRules.OPEN).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); 316// subset.getSlicing().setRules(slicingL.getRules() == SlicingRules.CLOSED || slicingR.getRules() == SlicingRules.CLOSED ? SlicingRules.OPEN : SlicingRules.CLOSED).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); 317// 318// // the superset is the union of the types 319// // the subset is the intersection of them 320// List<DefinitionNavigator> handled = new ArrayList<>(); 321// for (DefinitionNavigator t : left.slices()) { 322// DefinitionNavigator r = findMatchingSlice(right.slices(), t); 323// if (r == null) { 324// copySlice(outcome.superset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), t); 325// } else { 326// handled.add(r); 327// ret = compareElements(outcome, path+":"+t.current().getSliceName(), t, r, t.current().getSliceName()) && ret; 328// } 329// } 330// for (DefinitionNavigator t : right.slices()) { 331// if (!handled.contains(t)) { 332// copySlice(outcome.superset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), t); 333// } 334// } 335// } else if (slicingMatches(slicingL, slicingR)) { 336// // if it's the same, we can try matching the slices - though we might have to give up without getting matches correct 337// // there amy be implied consistency we can't reason about 338// throw new DefinitionException("Slicing matches but is not handled yet at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+")"); 339// } else { 340// // if the slicing is different, we can't compare them - or can we? 341// throw new DefinitionException("Slicing doesn't match at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+" / "+ProfileUtilities.summarizeSlicing(slicingR)+")"); 342// } 343// } 344// // todo: name 345// } 346// return ret; 347// 348// // TODO Auto-generated method stub 349// return null; 350 return def; 351 } 352 353 354 private boolean compareChildren(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError { 355 boolean def = false; 356 357 List<DefinitionNavigator> lc = left.children(); 358 List<DefinitionNavigator> rc = right.children(); 359 // it's possible that one of these profiles walks into a data type and the other doesn't 360 // if it does, we have to load the children for that data into the profile that doesn't 361 // walk into it 362 if (lc.isEmpty() && !rc.isEmpty() && right.current().getType().size() == 1 && left.hasTypeChildren(right.current().getType().get(0), left.getStructure())) 363 lc = left.childrenFromType(right.current().getType().get(0), right.getStructure()); 364 if (rc.isEmpty() && !lc.isEmpty() && left.current().getType().size() == 1 && right.hasTypeChildren(left.current().getType().get(0), right.getStructure())) 365 rc = right.childrenFromType(left.current().getType().get(0), left.getStructure()); 366 367 List<DefinitionNavigator> matchR = new ArrayList<>(); 368 for (DefinitionNavigator l : lc) { 369 DefinitionNavigator r = findInList(rc, l); 370 if (r == null) { 371 comp.getUnion().getSnapshot().getElement().add(l.current().copy()); 372 res.getChildren().add(new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(l.getStructure(), l.current()), vmI(IssueSeverity.INFORMATION, "Removed this element", path))); 373 } else { 374 matchR.add(r); 375 StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(l.getStructure(), l.current()), new ElementDefinitionNode(r.getStructure(), r.current())); 376 res.getChildren().add(sm); 377 def = compareElements(comp, sm, l.path(), null, l, r) || def; 378 } 379 } 380 for (DefinitionNavigator r : rc) { 381 if (!matchR.contains(r)) { 382 comp.getUnion().getSnapshot().getElement().add(r.current().copy()); 383 res.getChildren().add(new StructuralMatch<ElementDefinitionNode>(vmI(IssueSeverity.INFORMATION, "Added this element", path), new ElementDefinitionNode(r.getStructure(), r.current()))); 384 } 385 } 386 return def; 387 } 388 389 390 private boolean compareDiff(String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right, ProfileComparison res, Base parent) throws DefinitionException, FHIRFormatError, IOException { 391 assert(path != null); 392 assert(left != null); 393 assert(right != null); 394 assert(left.path().equals(right.path())); 395 396 boolean def = false; 397 boolean ch = false; 398 399 // not allowed to be different: 400// ruleEqual(comp, res, left.current().getDefaultValue(), right.current().getDefaultValue(), "defaultValue", path); 401// ruleEqual(comp, res, left.current().getMeaningWhenMissingElement(), right.current().getMeaningWhenMissingElement(), "meaningWhenMissing", path); 402// ruleEqual(comp, res, left.current().getIsModifierElement(), right.current().getIsModifierElement(), "isModifier", path); - this check belongs in the core 403// ruleEqual(comp, res, left.current().getIsSummaryElement(), right.current().getIsSummaryElement(), "isSummary", path); - so does this 404 405 ElementDefinition edl = left.current(); 406 ElementDefinition edr = right.current(); 407 if (edl == null && edr == null) { 408 // both are sparse at this point, do nothing 409 } else if (edl == null) { 410 session.markAdded(edr); 411 } else if (edr == null) { 412 session.markDeleted(right.parent(), "element", edl); 413 } else { 414 // descriptive properties from ElementDefinition 415 comparePrimitivesWithTracking("label", edl.getLabelElement(), edr.getLabelElement(), null, IssueSeverity.INFORMATION, null, edr); 416 comparePrimitivesWithTracking("sliceName", edl.getSliceNameElement(), edr.getSliceNameElement(), null, IssueSeverity.INFORMATION, null, edr); 417 comparePrimitivesWithTracking("sliceIsConstraining", edl.getSliceIsConstrainingElement(), edr.getSliceIsConstrainingElement(), null, IssueSeverity.INFORMATION, null, edr); 418 comparePrimitivesWithTracking("alias", edl.getAlias(), edr.getAlias(), null, IssueSeverity.INFORMATION, null, edr); 419 compareDataTypesWithTracking("code", edl.getCode(), edr.getCode(), null, IssueSeverity.INFORMATION, null, edr); 420 421 def = comparePrimitivesWithTracking("short", edl.getShortElement(), edr.getShortElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 422 def = comparePrimitivesWithTracking("definition", edl.getDefinitionElement(), edr.getDefinitionElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 423 def = comparePrimitivesWithTracking("comment", edl.getCommentElement(), edr.getCommentElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 424 def = comparePrimitivesWithTracking("requirements", edl.getRequirementsElement(), edr.getRequirementsElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 425 def = comparePrimitivesWithTracking("mustSupport", edl.getMustSupportElement(), edr.getMustSupportElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 426 def = comparePrimitivesWithTracking("meaningWhenMissing", edl.getMeaningWhenMissingElement(), edr.getMeaningWhenMissingElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 427 def = comparePrimitivesWithTracking("isModifierReason", edl.getIsModifierReasonElement(), edr.getIsModifierReasonElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 428 429 ch = comparePrimitivesWithTracking("min", edl.getMinElement(), edr.getMinElement(), null, IssueSeverity.ERROR, null, edr) || ch; 430 ch = comparePrimitivesWithTracking("max", edl.getMaxElement(), edr.getMaxElement(), null, IssueSeverity.ERROR, null, edr) || ch; 431 ch = compareDataTypesWithTracking("defaultValue", edl.getDefaultValue(), edr.getDefaultValue(), null, IssueSeverity.ERROR, null, edr) || ch; 432 ch = compareDataTypesWithTracking("fixed", edl.getFixed(), edr.getFixed(), null, IssueSeverity.ERROR, null, edr) || ch; 433 ch = compareDataTypesWithTracking("pattern", edl.getPattern(), edr.getPattern(), null, IssueSeverity.ERROR, null, edr) || ch; 434 ch = compareDataTypesWithTracking("minValue", edl.getMinValue(), edr.getMinValue(), null, IssueSeverity.ERROR, null, edr) || ch; 435 ch = compareDataTypesWithTracking("maxValue", edl.getMaxValue(), edr.getMaxValue(), null, IssueSeverity.ERROR, null, edr) || ch; 436 ch = comparePrimitivesWithTracking("maxLength", edl.getMaxLengthElement(), edr.getMaxLengthElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 437 ch = comparePrimitivesWithTracking("mustHaveValue", edl.getMustHaveValueElement(), edr.getMustHaveValueElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 438 ch = comparePrimitivesWithTracking("valueAlternatives", edl.getValueAlternatives(), edr.getValueAlternatives(), null, IssueSeverity.INFORMATION, null, edr) || ch; 439 ch = comparePrimitivesWithTracking("isModifier", edl.getIsModifierElement(), edr.getIsModifierElement(), null, IssueSeverity.INFORMATION, null, edr) || def; 440 441 def = compareTypes(path, sliceName, edl, edr, res) || def; 442 443 444 ElementDefinitionBindingComponent bl = edl.getBinding(); 445 ElementDefinitionBindingComponent br = edr.getBinding(); 446 if (bl == null && br == null) { 447 // both are sparse at this point, do nothing 448 } else if (bl == null) { 449 session.markAdded(edr); 450 } else if (br == null) { 451 session.markDeleted(right.parent(), "element", edl); 452 } else { 453 ch = comparePrimitivesWithTracking("strength", bl.getStrengthElement(), br.getStrengthElement(), null, IssueSeverity.ERROR, null, edr) || ch; 454 def = comparePrimitivesWithTracking("description", bl.getDescriptionElement(), br.getDescriptionElement(), null, IssueSeverity.ERROR, null, edr) || def; 455 ch = comparePrimitivesWithTracking("valueSet", bl.getValueSetElement(), br.getValueSetElement(), null, IssueSeverity.ERROR, null, edr) || ch; 456 // todo: additional 457 } 458 459 def = compareInvariants(path, sliceName, edl, edr, res) || def; 460 461 // main todos: 462 // invariants, slicing 463 // mappings 464 } 465 // add the children 466 if (ch) { 467 res.updateContentState(true); 468 } 469 def = compareDiffChildren(path, left, right, edr == null ? parent : edr, res) || def; 470// 471// // now process the slices 472// if (left.current().hasSlicing() || right.current().hasSlicing()) { 473// assert sliceName == null; 474// if (isExtension(left.path())) 475// return compareExtensions(outcome, path, superset, subset, left, right); 476// // return true; 477// else { 478// ElementDefinitionSlicingComponent slicingL = left.current().getSlicing(); 479// ElementDefinitionSlicingComponent slicingR = right.current().getSlicing(); 480// // 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. 481// if (left.current().hasSlicing() && !right.current().hasSlicing()) { 482// // 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) 483// // the minimum set is the slicing specified in the slicer 484// subset.setSlicing(slicingL); 485// // stick everything from the right to do with the slices to the subset 486// copySlices(outcome.subset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), left.slices()); 487// } else if (!left.current().hasSlicing() && right.current().hasSlicing()) { 488// // 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) 489// // the minimum set is the slicing specified in the slicer 490// subset.setSlicing(slicingR); 491// // stick everything from the right to do with the slices to the subset 492// copySlices(outcome.subset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), right.slices()); 493// } else if (isTypeSlicing(slicingL) || isTypeSlicing(slicingR)) { 494// superset.getSlicing().setRules(SlicingRules.OPEN).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); 495// subset.getSlicing().setRules(slicingL.getRules() == SlicingRules.CLOSED || slicingR.getRules() == SlicingRules.CLOSED ? SlicingRules.OPEN : SlicingRules.CLOSED).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); 496// 497// // the superset is the union of the types 498// // the subset is the intersection of them 499// List<DefinitionNavigator> handled = new ArrayList<>(); 500// for (DefinitionNavigator t : left.slices()) { 501// DefinitionNavigator r = findMatchingSlice(right.slices(), t); 502// if (r == null) { 503// copySlice(outcome.superset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), t); 504// } else { 505// handled.add(r); 506// ret = compareElements(outcome, path+":"+t.current().getSliceName(), t, r, t.current().getSliceName()) && ret; 507// } 508// } 509// for (DefinitionNavigator t : right.slices()) { 510// if (!handled.contains(t)) { 511// copySlice(outcome.superset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), t); 512// } 513// } 514// } else if (slicingMatches(slicingL, slicingR)) { 515// // if it's the same, we can try matching the slices - though we might have to give up without getting matches correct 516// // there amy be implied consistency we can't reason about 517// throw new DefinitionException("Slicing matches but is not handled yet at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+")"); 518// } else { 519// // if the slicing is different, we can't compare them - or can we? 520// throw new DefinitionException("Slicing doesn't match at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+" / "+ProfileUtilities.summarizeSlicing(slicingR)+")"); 521// } 522// } 523// // todo: name 524// } 525// return ret; 526// 527// // TODO Auto-generated method stub 528// return null; 529 return def; 530 } 531 532 private boolean compareDiffChildren(String path, DefinitionNavigator left, DefinitionNavigator right, Base parent, ProfileComparison res) throws DefinitionException, IOException, FHIRFormatError { 533 boolean def = false; 534 535 List<DefinitionNavigator> lc = left.children(); 536 List<DefinitionNavigator> rc = right.children(); 537 538 List<DefinitionNavigator> matchR = new ArrayList<>(); 539 for (DefinitionNavigator l : lc) { 540 DefinitionNavigator r = findInList(rc, l); 541 if (r == null) { 542 session.markDeleted(parent, "element", l.current()); 543 res.updateContentState(true); 544 } else { 545 matchR.add(r); 546 def = compareDiff(l.path(), null, l, r, res, parent) || def; 547 } 548 } 549 for (DefinitionNavigator r : rc) { 550 if (!matchR.contains(r)) { 551 session.markAdded(r.current()); 552 res.updateContentState(true); 553 } 554 } 555 return def; 556 } 557 558 private DefinitionNavigator findInList(List<DefinitionNavigator> rc, DefinitionNavigator l) { 559 String s = l.getId(); 560 for (DefinitionNavigator t : rc) { 561 String ts = t.getId(); 562 if (tail(ts).equals(tail(s))) { 563 return t; 564 } 565 } 566 return null; 567 } 568 569 private boolean compareTypes(String path, String sliceName, ElementDefinition left, ElementDefinition right, ProfileComparison res) { 570 boolean def = false; 571 572 List<TypeRefComponent> matchR = new ArrayList<>(); 573 for (TypeRefComponent l : left.getType()) { 574 TypeRefComponent r = findInList(right.getType(), l); 575 if (r == null) { 576 session.markDeleted(right, "type", l); 577 res.updateContentState(true); 578 } else { 579 matchR.add(r); 580 def = compareType(path+".type", l, r, res) || def; 581 } 582 } 583 for (TypeRefComponent r : right.getType()) { 584 if (!matchR.contains(r)) { 585 session.markAdded(r); 586 res.updateContentState(true); 587 } 588 } 589 return def; 590 } 591 592 private TypeRefComponent findInList(List<TypeRefComponent> rc, TypeRefComponent l) { 593 for (TypeRefComponent t : rc) { 594 if (t.getCodeElement().equalsDeep(l.getCodeElement())) { 595 return t; 596 } 597 } 598 return null; 599 } 600 601 602 private boolean compareType(String string, TypeRefComponent l, TypeRefComponent r, ProfileComparison res) { 603 boolean def = false; 604 boolean ch = false; 605 // codes must match 606 ch = comparePrimitivesWithTracking("profile", l.getProfile(), r.getProfile(), null, IssueSeverity.ERROR, null, r) || ch; 607 ch = comparePrimitivesWithTracking("targetProfile", l.getTargetProfile(), r.getTargetProfile(), null, IssueSeverity.ERROR, null, r) || ch; 608 ch = comparePrimitivesWithTracking("aggregation", l.getAggregation(), r.getAggregation(), null, IssueSeverity.ERROR, null, r) || ch; 609 def = comparePrimitivesWithTracking("versioning", l.getVersioningElement(), r.getVersioningElement(), null, IssueSeverity.INFORMATION, null, r) || def; 610 if (ch) { 611 res.updateContentState(true); 612 } 613 return def; 614 } 615 616 617 private boolean compareInvariants(String path, String sliceName, ElementDefinition left, ElementDefinition right, ProfileComparison res) { 618 boolean def = false; 619 620 List<ElementDefinitionConstraintComponent> matchR = new ArrayList<>(); 621 for (ElementDefinitionConstraintComponent l : left.getConstraint()) { 622 ElementDefinitionConstraintComponent r = findInList(right.getConstraint(), l); 623 if (r == null) { 624 session.markDeleted(right, "invariant", l); 625 res.updateContentState(true); 626 } else { 627 matchR.add(r); 628 def = compareInvariant(path+".type", l, r, res) || def; 629 } 630 } 631 for (ElementDefinitionConstraintComponent r : right.getConstraint()) { 632 if (!matchR.contains(r)) { 633 session.markAdded(r); 634 res.updateContentState(true); 635 } 636 } 637 return def; 638 } 639 640 private ElementDefinitionConstraintComponent findInList(List<ElementDefinitionConstraintComponent> rc, ElementDefinitionConstraintComponent l) { 641 for (ElementDefinitionConstraintComponent t : rc) { 642 if (t.getKeyElement().equalsDeep(l.getKeyElement())) { 643 return t; 644 } 645 } 646 return null; 647 } 648 649 650 private boolean compareInvariant(String string, ElementDefinitionConstraintComponent l, ElementDefinitionConstraintComponent r, ProfileComparison res) { 651 boolean def = false; 652 boolean ch = false; 653 // codes must match 654 def = comparePrimitivesWithTracking("requirements", l.getRequirementsElement(), r.getRequirementsElement(), null, IssueSeverity.INFORMATION, null, r) || def; 655 ch = comparePrimitivesWithTracking("severity", l.getSeverityElement(), r.getSeverityElement(), null, IssueSeverity.ERROR, null, r) || ch; 656 comparePrimitivesWithTracking("suppress", l.getSuppressElement(), r.getSuppressElement(), null, IssueSeverity.INFORMATION, null, r); 657 def = comparePrimitivesWithTracking("human", l.getHumanElement(), r.getHumanElement(), null, IssueSeverity.INFORMATION, null, r) || def; 658 ch = comparePrimitivesWithTracking("expression", l.getExpressionElement(), r.getExpressionElement(), null, IssueSeverity.ERROR, null, r) || ch; 659 if (ch) { 660 res.updateContentState(true); 661 } 662 return def; 663 } 664 665// private void ruleEqual(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, DataType vLeft, DataType vRight, String name, String path) throws IOException { 666// if (vLeft == null && vRight == null) { 667// // nothing 668// } else if (vLeft == null) { 669// vm(IssueSeverity.ERROR, "Added "+name, path, comp.getMessages(), res.getMessages()); 670// } else if (vRight == null) { 671// vm(IssueSeverity.ERROR, "Removed "+name, path, comp.getMessages(), res.getMessages()); 672// } else if (!Base.compareDeep(vLeft, vRight, false)) { 673// vm(IssueSeverity.ERROR, name+" must be the same ("+toString(vLeft, true)+"/"+toString(vRight, false)+")", path, comp.getMessages(), res.getMessages()); 674// } 675// } 676// 677 private String toString(DataType val, boolean left) throws IOException { 678 if (val instanceof PrimitiveType) 679 return "'" + ((PrimitiveType) val).getValueAsString()+"'"; 680 681 IParser jp = new JsonParser(); 682 return jp.composeString(val, "value"); 683 } 684 685 private String stripLinks(String s) { 686 while (s.contains("](")) { 687 int i = s.indexOf("]("); 688 int j = s.substring(i).indexOf(")"); 689 if (j == -1) 690 return s; 691 else 692 s = s.substring(0, i+1)+s.substring(i+j+1); 693 } 694 return s; 695 } 696 697 private boolean rule(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, boolean test, String path, String message) { 698 if (!test) { 699 vm(IssueSeverity.ERROR, message, path, comp.getMessages(), res.getMessages()); 700 } 701 return test; 702 } 703 704 private String mergeText(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String name, String left, String right, boolean isError) { 705 if (left == null && right == null) 706 return null; 707 if (left == null) 708 return right; 709 if (right == null) 710 return left; 711 left = stripLinks(left); 712 right = stripLinks(right); 713 if (left.equalsIgnoreCase(right)) 714 return left; 715 return "left: "+left+"; right: "+right; 716 } 717 718 private List<Coding> mergeCodings(List<Coding> left, List<Coding> right) { 719 List<Coding> result = new ArrayList<Coding>(); 720 result.addAll(left); 721 for (Coding c : right) { 722 boolean found = false; 723 for (Coding ct : left) 724 if (Utilities.equals(c.getSystem(), ct.getSystem()) && Utilities.equals(c.getCode(), ct.getCode())) 725 found = true; 726 if (!found) 727 result.add(c); 728 } 729 return result; 730 } 731 732 private List<StringType> mergeStrings(List<StringType> left, List<StringType> right) { 733 List<StringType> result = new ArrayList<StringType>(); 734 result.addAll(left); 735 for (StringType c : right) { 736 boolean found = false; 737 for (StringType ct : left) 738 if (Utilities.equals(c.getValue(), ct.getValue())) 739 found = true; 740 if (!found) 741 result.add(c); 742 } 743 return result; 744 } 745 746 private List<ElementDefinitionMappingComponent> mergeMappings(List<ElementDefinitionMappingComponent> left, List<ElementDefinitionMappingComponent> right) { 747 List<ElementDefinitionMappingComponent> result = new ArrayList<ElementDefinitionMappingComponent>(); 748 result.addAll(left); 749 for (ElementDefinitionMappingComponent c : right) { 750 boolean found = false; 751 for (ElementDefinitionMappingComponent ct : left) 752 if (Utilities.equals(c.getIdentity(), ct.getIdentity()) && Utilities.equals(c.getLanguage(), ct.getLanguage()) && Utilities.equals(c.getMap(), ct.getMap())) 753 found = true; 754 if (!found) 755 result.add(c); 756 } 757 return result; 758 } 759 760 private int intersectMin(int left, int right) { 761 if (left > right) 762 return left; 763 else 764 return right; 765 } 766 767 private void checkMinMax(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, int leftMin, int rightMin, int leftMax, int rightMax) { 768 if (leftMin != rightMin) { 769 if (leftMin == 0) { 770 vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); 771 } else if (rightMin == 0) { 772 vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); 773 } else { 774 vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); 775 } 776 } 777 if (leftMax != rightMax) { 778 if (leftMax == Integer.MAX_VALUE) { 779 vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); 780 } else if (rightMax == Integer.MAX_VALUE) { 781 vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); 782 } else { 783 vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); 784 } 785 } 786// rule(comp, res, subset.getMax().equals("*") || Integer.parseInt(subset.getMax()) >= subset.getMin(), path, "Cardinality Mismatch: "+card(left)+"/"+card(right)); 787 788 // cross comparison - if max > min in either direction, there can be no instances that are valid against both 789 if (leftMax < rightMin) { 790 vm(IssueSeverity.ERROR, "Element minimum cardinalities conflict: '"+leftMin+".."+leftMax+"' vs '"+rightMin+".."+rightMax+"': No instances can be valid against both profiles", path, comp.getMessages(), res.getMessages()); 791 } 792 if (rightMax < leftMin) { 793 vm(IssueSeverity.ERROR, "Element minimum cardinalities conflict: '"+leftMin+".."+leftMax+"' vs '"+rightMin+".."+rightMax+"': No instances can be valid against both profiles", path, comp.getMessages(), res.getMessages()); 794 } 795 } 796 797 private int unionMin(int left, int right) { 798 if (left > right) 799 return right; 800 else 801 return left; 802 } 803 804 private String intersectMax(int l, int r, String left, String right) { 805 if (l < r) 806 return left; 807 else 808 return right; 809 } 810 811 private String unionMax(int l, int r, String left, String right) { 812 if (l < r) 813 return right; 814 else 815 return left; 816 } 817 818 private IntegerType intersectMaxLength(int left, int right) { 819 if (left == 0) 820 left = Integer.MAX_VALUE; 821 if (right == 0) 822 right = Integer.MAX_VALUE; 823 if (left < right) 824 return left == Integer.MAX_VALUE ? null : new IntegerType(left); 825 else 826 return right == Integer.MAX_VALUE ? null : new IntegerType(right); 827 } 828 829 private IntegerType unionMaxLength(int left, int right) { 830 if (left == 0) 831 left = Integer.MAX_VALUE; 832 if (right == 0) 833 right = Integer.MAX_VALUE; 834 if (left < right) 835 return right == Integer.MAX_VALUE ? null : new IntegerType(right); 836 else 837 return left == Integer.MAX_VALUE ? null : new IntegerType(left); 838 } 839 840 private String card(DefinitionNavigator defn) { 841 return Integer.toString(defn.current().getMin())+".."+defn.current().getMax(); 842 } 843 844 private Collection<? extends TypeRefComponent> unionTypes(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, List<TypeRefComponent> left, List<TypeRefComponent> right, Resource leftSrc, Resource rightSrc) throws DefinitionException, IOException, FHIRFormatError { 845 List<TypeRefComponent> result = new ArrayList<TypeRefComponent>(); 846 for (TypeRefComponent l : left) 847 checkAddTypeUnion(comp, res, path, result, l, session.getContextLeft(), leftSrc); 848 for (TypeRefComponent r : right) 849 checkAddTypeUnion(comp, res, path, result, r, session.getContextRight(), rightSrc); 850 return result; 851 } 852 853 private void checkAddTypeUnion(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, List<TypeRefComponent> results, TypeRefComponent nw, IWorkerContext ctxt, Resource nwSource) throws DefinitionException, IOException, FHIRFormatError { 854 boolean pfound = false; 855 boolean tfound = false; 856 nw = nw.copy(); 857 for (TypeRefComponent ex : results) { 858 if (Utilities.equals(ex.getWorkingCode(), nw.getWorkingCode())) { 859 for (Enumeration<AggregationMode> a : nw.getAggregation()) { 860 if (!ex.hasAggregation(a.getValue())) { 861 ex.addAggregation(a.getValue()); 862 } 863 } 864 if (!ex.hasProfile() && !nw.hasProfile()) 865 pfound = true; 866 else if (!ex.hasProfile()) { 867 pfound = true; 868 } else if (!nw.hasProfile()) { 869 pfound = true; 870 ex.setProfile(null); 871 } else { 872 // both have profiles. Is one derived from the other? 873 StructureDefinition sdex = ((IWorkerContext) ex.getUserData("ctxt")).fetchResource(StructureDefinition.class, ex.getProfile().get(0).getValue(), nwSource); 874 StructureDefinition sdnw = ctxt.fetchResource(StructureDefinition.class, nw.getProfile().get(0).getValue(), nwSource); 875 if (sdex != null && sdnw != null) { 876 if (sdex.getUrl().equals(sdnw.getUrl())) { 877 pfound = true; 878 } else if (derivesFrom(sdex, sdnw, ((IWorkerContext) ex.getUserData("ctxt")))) { 879 ex.setProfile(nw.getProfile()); 880 pfound = true; 881 } else if (derivesFrom(sdnw, sdex, ctxt)) { 882 pfound = true; 883 } else if (sdnw.getSnapshot().getElement().get(0).getPath().equals(sdex.getSnapshot().getElement().get(0).getPath())) { 884 ProfileComparison compP = (ProfileComparison) session.compare(sdex, sdnw); 885 if (compP != null && compP.getUnion() != null) { // might be null if circular 886 pfound = true; 887 ex.addProfile("#"+compP.getId()); 888 } 889 } 890 } 891 } 892 if (!ex.hasTargetProfile() && !nw.hasTargetProfile()) 893 tfound = true; 894 else if (!ex.hasTargetProfile()) { 895 tfound = true; 896 } else if (!nw.hasTargetProfile()) { 897 tfound = true; 898 ex.setTargetProfile(null); 899 } else { 900 // both have profiles. Is one derived from the other? 901 StructureDefinition sdex = ((IWorkerContext) ex.getUserData("ctxt")).fetchResource(StructureDefinition.class, ex.getTargetProfile().get(0).getValue(), nwSource); 902 StructureDefinition sdnw = ctxt.fetchResource(StructureDefinition.class, nw.getTargetProfile().get(0).getValue(), nwSource); 903 if (sdex != null && sdnw != null) { 904 if (matches(sdex, sdnw)) { 905 tfound = true; 906 } else if (derivesFrom(sdex, sdnw, ((IWorkerContext) ex.getUserData("ctxt")))) { 907 ex.setTargetProfile(nw.getTargetProfile()); 908 tfound = true; 909 } else if (derivesFrom(sdnw, sdex, ctxt)) { 910 tfound = true; 911 } else if (sdnw.getSnapshot().getElement().get(0).getPath().equals(sdex.getSnapshot().getElement().get(0).getPath())) { 912 ResourceComparison cmp = session.compare(sdex, sdnw); 913 if (cmp instanceof ProfileComparison) { 914 ProfileComparison compP = (ProfileComparison) cmp; 915 if (compP.getUnion() != null) { 916 tfound = true; 917 ex.addTargetProfile("#"+compP.getId()); 918 } 919 } else { 920 // ? 921 } 922 } 923 } 924 } 925 } 926 } 927 if (!tfound || !pfound) { 928 nw.setUserData("ctxt", ctxt); 929 results.add(nw); 930 } 931 } 932 933 private boolean matches(StructureDefinition s1, StructureDefinition s2) { 934 if (!s1.getUrl().equals(s2.getUrl())) { 935 return false; 936 } 937 if (s1.getDerivation() == TypeDerivationRule.SPECIALIZATION && s2.getDerivation() == TypeDerivationRule.SPECIALIZATION) { 938 return true; // arbitrary; we're just not interested in pursuing cross version differences 939 } 940 if (s1.hasVersion()) { 941 return s1.getVersion().equals(s2.getVersion()); 942 } else { 943 return !s2.hasVersion(); 944 } 945 } 946 947 private boolean derivesFrom(StructureDefinition left, StructureDefinition right, IWorkerContext ctxt) { 948 StructureDefinition sd = left; 949 while (sd != null) { 950 if (right.getUrl().equals(sd.getBaseDefinition())) { 951 return true; 952 } 953 sd = sd.hasBaseDefinition() ? ctxt.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd) : null; 954 } 955 return false; 956 } 957 958 private Collection<? extends TypeRefComponent> intersectTypes(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, ElementDefinition ed, String path, List<TypeRefComponent> left, List<TypeRefComponent> right) throws DefinitionException, IOException, FHIRFormatError { 959 List<TypeRefComponent> result = new ArrayList<TypeRefComponent>(); 960 for (TypeRefComponent l : left) { 961 boolean pfound = false; 962 boolean tfound = false; 963 TypeRefComponent c = l.copy(); 964 for (TypeRefComponent r : right) { 965 if (!l.hasProfile() && !r.hasProfile()) { 966 pfound = true; 967 } else if (!r.hasProfile()) { 968 pfound = true; 969 } else if (!l.hasProfile()) { 970 pfound = true; 971 c.setProfile(r.getProfile()); 972 } else { 973 StructureDefinition sdl = resolveProfile(comp, res, path, l.getProfile().get(0).getValue(), comp.getLeft().getName(), session.getContextLeft(), comp.getLeft()); 974 StructureDefinition sdr = resolveProfile(comp, res, path, r.getProfile().get(0).getValue(), comp.getRight().getName(), session.getContextRight(), comp.getRight()); 975 if (sdl != null && sdr != null) { 976 if (sdl == sdr) { 977 pfound = true; 978 } else if (derivesFrom(sdl, sdr, session.getContextLeft())) { 979 pfound = true; 980 } else if (derivesFrom(sdr, sdl, session.getContextRight())) { 981 c.setProfile(r.getProfile()); 982 pfound = true; 983 } else if (sdl.getType().equals(sdr.getType())) { 984 ResourceComparison cmp = session.compare(sdl, sdr); 985 if (cmp instanceof ProfileComparison) { 986 ProfileComparison compP = (ProfileComparison) cmp; 987 if (compP != null && compP.getIntersection() != null) { 988 pfound = true; 989 c.addProfile("#"+compP.getId()); 990 } 991 } else { 992 // not sure how to handle this error? 993 } 994 } 995 } 996 } 997 if (!l.hasTargetProfile() && !r.hasTargetProfile()) { 998 tfound = true; 999 } else if (!r.hasTargetProfile()) { 1000 tfound = true; 1001 } else if (!l.hasTargetProfile()) { 1002 tfound = true; 1003 c.setTargetProfile(r.getTargetProfile()); 1004 } else { 1005 StructureDefinition sdl = resolveProfile(comp, res, path, l.getTargetProfile().get(0).getValue(), comp.getLeft().getName(), session.getContextLeft(), comp.getLeft()); 1006 StructureDefinition sdr = resolveProfile(comp, res, path, r.getTargetProfile().get(0).getValue(), comp.getRight().getName(), session.getContextRight(), comp.getRight()); 1007 if (sdl != null && sdr != null) { 1008 if (matches(sdl, sdr)) { 1009 tfound = true; 1010 } else if (derivesFrom(sdl, sdr, session.getContextLeft())) { 1011 tfound = true; 1012 } else if (derivesFrom(sdr, sdl, session.getContextRight())) { 1013 c.setTargetProfile(r.getTargetProfile()); 1014 tfound = true; 1015 } else if (sdl.getType().equals(sdr.getType())) { 1016 ProfileComparison compP = (ProfileComparison) session.compare(sdl, sdr); 1017 if (compP != null && compP.getIntersection() != null) { 1018 tfound = true; 1019 c.addTargetProfile("#"+compP.getId()); 1020 } 1021 } 1022 } 1023 } 1024 if (pfound && tfound) { 1025 for (Enumeration<AggregationMode> a : l.getAggregation()) { 1026 if (!r.hasAggregation(a.getValue())) { 1027 c.getAggregation().removeIf(n -> n.getValue() == a.getValue()); 1028 } 1029 } 1030 } 1031 } 1032 if (pfound && tfound) { 1033 result.add(c); 1034 } 1035 } 1036 return result; 1037 } 1038 1039 private String typeCode(DefinitionNavigator defn) { 1040 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 1041 for (TypeRefComponent t : defn.current().getType()) 1042 b.append(t.getWorkingCode()+(t.hasProfile() ? "("+t.getProfile()+")" : "")+(t.hasTargetProfile() ? "("+t.getTargetProfile()+")" : "")); // todo: other properties 1043 return b.toString(); 1044 } 1045 1046 private boolean compareBindings(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, ElementDefinition subset, ElementDefinition superset, String path, ElementDefinition lDef, ElementDefinition rDef, Resource leftSrc, Resource rightSrc) throws FHIRFormatError, DefinitionException, IOException { 1047 assert(lDef.hasBinding() || rDef.hasBinding()); 1048 if (!lDef.hasBinding()) { 1049 subset.setBinding(rDef.getBinding()); 1050 // technically, the super set is unbound, but that's not very useful - so we use the provided on as an example 1051 superset.setBinding(rDef.getBinding().copy()); 1052 superset.getBinding().setStrength(BindingStrength.EXAMPLE); 1053 return true; 1054 } 1055 if (!rDef.hasBinding()) { 1056 subset.setBinding(lDef.getBinding()); 1057 superset.setBinding(lDef.getBinding().copy()); 1058 superset.getBinding().setStrength(BindingStrength.EXAMPLE); 1059 return true; 1060 } 1061 ElementDefinitionBindingComponent left = lDef.getBinding(); 1062 ElementDefinitionBindingComponent right = rDef.getBinding(); 1063 if (Base.compareDeep(left, right, false)) { 1064 subset.setBinding(left); 1065 superset.setBinding(right); 1066 } 1067 1068 // if they're both examples/preferred then: 1069 // subset: left wins if they're both the same 1070 // superset: 1071 if (isPreferredOrExample(left) && isPreferredOrExample(right)) { 1072 if (right.getStrength() == BindingStrength.PREFERRED && left.getStrength() == BindingStrength.EXAMPLE && !Base.compareDeep(left.getValueSet(), right.getValueSet(), false)) { 1073 vm(IssueSeverity.INFORMATION, "Example/preferred bindings differ at "+path+" using binding from "+comp.getRight().getName(), path, comp.getMessages(), res.getMessages()); 1074 subset.setBinding(right); 1075 superset.setBinding(unionBindings(comp, res, path, left, right, leftSrc, rightSrc)); 1076 } else { 1077 if ((right.getStrength() != BindingStrength.EXAMPLE || left.getStrength() != BindingStrength.EXAMPLE) && !Base.compareDeep(left.getValueSet(), right.getValueSet(), false) ) { 1078 vm(IssueSeverity.INFORMATION, "Example/preferred bindings differ at "+path+" using binding from "+comp.getLeft().getName(), path, comp.getMessages(), res.getMessages()); 1079 } 1080 subset.setBinding(left); 1081 superset.setBinding(unionBindings(comp, res, path, left, right, leftSrc, rightSrc)); 1082 } 1083 return true; 1084 } 1085 // if either of them are extensible/required, then it wins 1086 if (isPreferredOrExample(left)) { 1087 subset.setBinding(right); 1088 superset.setBinding(unionBindings(comp, res, path, left, right, leftSrc, rightSrc)); 1089 return true; 1090 } 1091 if (isPreferredOrExample(right)) { 1092 subset.setBinding(left); 1093 superset.setBinding(unionBindings(comp, res, path, left, right, leftSrc, rightSrc)); 1094 return true; 1095 } 1096 1097 // ok, both are extensible or required. 1098 ElementDefinitionBindingComponent subBinding = new ElementDefinitionBindingComponent(); 1099 subset.setBinding(subBinding); 1100 ElementDefinitionBindingComponent superBinding = new ElementDefinitionBindingComponent(); 1101 superset.setBinding(superBinding); 1102 subBinding.setDescription(mergeText(comp, res, path, "description", left.getDescription(), right.getDescription(), false)); 1103 superBinding.setDescription(mergeText(comp, res, path, "description", left.getDescription(), right.getDescription(), false)); 1104 if (left.getStrength() == BindingStrength.REQUIRED || right.getStrength() == BindingStrength.REQUIRED) 1105 subBinding.setStrength(BindingStrength.REQUIRED); 1106 else 1107 subBinding.setStrength(BindingStrength.EXTENSIBLE); 1108 if (left.getStrength() == BindingStrength.EXTENSIBLE || right.getStrength() == BindingStrength.EXTENSIBLE) 1109 superBinding.setStrength(BindingStrength.EXTENSIBLE); 1110 else 1111 superBinding.setStrength(BindingStrength.REQUIRED); 1112 1113 if (Base.compareDeep(left.getValueSet(), right.getValueSet(), false)) { 1114 subBinding.setValueSet(left.getValueSet()); 1115 superBinding.setValueSet(left.getValueSet()); 1116 return true; 1117 } else if (!left.hasValueSet()) { 1118 vm(IssueSeverity.ERROR, "No left Value set at "+path, path, comp.getMessages(), res.getMessages()); 1119 return true; 1120 } else if (!right.hasValueSet()) { 1121 vm(IssueSeverity.ERROR, "No right Value set at "+path, path, comp.getMessages(), res.getMessages()); 1122 return true; 1123 } else { 1124 // ok, now we compare the value sets. This may be unresolvable. 1125 ValueSet lvs = resolveVS(comp.getLeft(), left.getValueSet(), leftSrc, session.getContextLeft()); 1126 ValueSet rvs = resolveVS(comp.getRight(), right.getValueSet(), rightSrc, session.getContextRight()); 1127 if (lvs == null) { 1128 vm(IssueSeverity.ERROR, "Unable to resolve left value set "+left.getValueSet().toString()+" at "+path, path, comp.getMessages(), res.getMessages()); 1129 return true; 1130 } else if (rvs == null) { 1131 vm(IssueSeverity.ERROR, "Unable to resolve right value set "+right.getValueSet().toString()+" at "+path, path, comp.getMessages(), res.getMessages()); 1132 return true; 1133 } else if (sameValueSets(lvs, rvs)) { 1134 subBinding.setValueSet(lvs.getUrl()); 1135 superBinding.setValueSet(lvs.getUrl()); 1136 } else { 1137 ValueSetComparison compP = (ValueSetComparison) session.compare(lvs, rvs); 1138 if (compP != null) { 1139 subBinding.setValueSet(compP.getIntersection().getUrl()); 1140 superBinding.setValueSet(compP.getUnion().getUrl()); 1141 } 1142 } 1143 } 1144 return false; 1145 } 1146 1147 private boolean sameValueSets(ValueSet lvs, ValueSet rvs) { 1148 if (!lvs.getUrl().equals(rvs.getUrl())) { 1149 return false; 1150 } 1151 if (isCore(lvs) && isCore(rvs)) { 1152 return true; 1153 } 1154 if (lvs.hasVersion()) { 1155 if (!lvs.getVersion().equals(rvs.getVersion())) { 1156 return false; 1157 } else if (!rvs.hasVersion()) { 1158 return false; 1159 } 1160 } 1161 return true; 1162 } 1163 1164 private boolean isCore(ValueSet vs) { 1165 return vs.getUrl().startsWith("http://hl7.org/fhir/ValueSet"); 1166 } 1167 1168 private List<ElementDefinitionConstraintComponent> intersectConstraints(String path, List<ElementDefinitionConstraintComponent> left, List<ElementDefinitionConstraintComponent> right) { 1169 List<ElementDefinitionConstraintComponent> result = new ArrayList<ElementDefinitionConstraintComponent>(); 1170 for (ElementDefinitionConstraintComponent l : left) { 1171 boolean found = false; 1172 for (ElementDefinitionConstraintComponent r : right) 1173 if (Utilities.equals(r.getId(), l.getId()) || (Utilities.equals(r.getExpression(), l.getExpression()) && r.getSeverity() == l.getSeverity())) 1174 found = true; 1175 if (found) 1176 result.add(l); 1177 } 1178 return result; 1179 } 1180 1181 // we can't really know about constraints. We create warnings, and collate them 1182 private List<ElementDefinitionConstraintComponent> unionConstraints(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, List<ElementDefinitionConstraintComponent> left, List<ElementDefinitionConstraintComponent> right) { 1183 List<ElementDefinitionConstraintComponent> result = new ArrayList<ElementDefinitionConstraintComponent>(); 1184 for (ElementDefinitionConstraintComponent l : left) { 1185 boolean found = false; 1186 for (ElementDefinitionConstraintComponent r : right) 1187 if (Utilities.equals(r.getId(), l.getId()) || (Utilities.equals(r.getExpression(), l.getExpression()) && r.getSeverity() == l.getSeverity())) 1188 found = true; 1189 if (!found) { 1190 if (!Utilities.existsInList(l.getExpression(), "hasValue() or (children().count() > id.count())", "extension.exists() != value.exists()")) { 1191 vm(IssueSeverity.INFORMATION, "StructureDefinition "+comp.getLeft().getName()+" has a constraint that is removed in "+comp.getRight().getName()+" and it is uncertain whether they are compatible ("+l.getExpression()+")", path, comp.getMessages(), res.getMessages()); 1192 } 1193 } 1194 result.add(l); 1195 } 1196 for (ElementDefinitionConstraintComponent r : right) { 1197 boolean found = false; 1198 for (ElementDefinitionConstraintComponent l : left) 1199 if (Utilities.equals(r.getId(), l.getId()) || (Utilities.equals(r.getExpression(), l.getExpression()) && r.getSeverity() == l.getSeverity())) 1200 found = true; 1201 if (!found) { 1202 if (!Utilities.existsInList(r.getExpression(), "hasValue() or (children().count() > id.count())", "extension.exists() != value.exists()")) { 1203 vm(IssueSeverity.INFORMATION, "StructureDefinition "+comp.getRight().getName()+" has added constraint that is not found in "+comp.getLeft().getName()+" and it is uncertain whether they are compatible ("+r.getExpression()+")", path, comp.getMessages(), res.getMessages()); 1204 } 1205 } 1206 } 1207 return result; 1208 } 1209 1210 private StructureDefinition resolveProfile(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String url, String name, IWorkerContext ctxt, Resource urlSource) { 1211 StructureDefinition sd = ctxt.fetchResource(StructureDefinition.class, url, urlSource); 1212 if (sd == null) { 1213 ValidationMessage vm = vmI(IssueSeverity.WARNING, "Unable to resolve profile "+url+" in profile "+name, path); 1214 } 1215 return sd; 1216 } 1217 1218 private boolean isPreferredOrExample(ElementDefinitionBindingComponent binding) { 1219 return binding.getStrength() == BindingStrength.EXAMPLE || binding.getStrength() == BindingStrength.PREFERRED; 1220 } 1221 1222 private ElementDefinitionBindingComponent unionBindings(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, ElementDefinitionBindingComponent left, ElementDefinitionBindingComponent right, Resource leftSrc, Resource rightSrc) throws FHIRFormatError, DefinitionException, IOException { 1223 ElementDefinitionBindingComponent union = new ElementDefinitionBindingComponent(); 1224 if (left.getStrength().compareTo(right.getStrength()) < 0) 1225 union.setStrength(left.getStrength()); 1226 else 1227 union.setStrength(right.getStrength()); 1228 union.setDescription(mergeText(comp, res, path, "binding.description", left.getDescription(), right.getDescription(), false)); 1229 if (Base.compareDeep(left.getValueSet(), right.getValueSet(), false)) 1230 union.setValueSet(left.getValueSet()); 1231 else { 1232 ValueSet lvs = resolveVS(comp.getLeft(), left.getValueSet(), leftSrc, session.getContextLeft()); 1233 ValueSet rvs = resolveVS(comp.getRight(), right.getValueSet(), rightSrc, session.getContextRight()); 1234 if (lvs != null && rvs != null) { 1235 ValueSetComparison compP = (ValueSetComparison) session.compare(lvs, rvs); 1236 if (compP != null) { 1237 union.setValueSet(compP.getUnion().getUrl()); 1238 } 1239 } else if (lvs != null) { 1240 union.setValueSet(lvs.getUrl()); 1241 } else if (rvs != null) { 1242 union.setValueSet(rvs.getUrl()); 1243 } 1244 } 1245 return union; 1246 } 1247 1248 private ValueSet resolveVS(StructureDefinition ctxtLeft, String vsRef, Resource src, IWorkerContext ctxt) { 1249 if (vsRef == null) 1250 return null; 1251 return ctxt.fetchResource(ValueSet.class, vsRef, src); 1252 } 1253 1254 public XhtmlNode renderStructure(ProfileComparison comp, String id, String prefix, String corePath) throws FHIRException, IOException { 1255 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(session.getI18n(), Utilities.path("[tmp]", "compare"), false, true, "cmp"); 1256 TableModel model = gen.initComparisonTable(corePath, id); 1257 genElementComp(null /* come back to this later */, null /* come back to this later */, gen, model.getRows(), comp.combined, corePath, prefix, null, true); 1258 return gen.generate(model, prefix, 0, null); 1259 } 1260 1261 public XhtmlNode renderUnion(ProfileComparison comp, String id, String prefix, String corePath) throws FHIRException, IOException { 1262 StructureDefinitionRenderer sdr = new StructureDefinitionRenderer(new RenderingContext(utilsLeft.getContext(), null, utilsLeft.getTerminologyServiceOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER).setPkp(this)); 1263 return sdr.generateTable(new RenderingStatus(), corePath, comp.union, false, prefix, false, id, true, corePath, prefix, false, true, null, false, sdr.getContext().withUniqueLocalPrefix("u"), "u", null); 1264 } 1265 1266 1267 public XhtmlNode renderIntersection(ProfileComparison comp, String id, String prefix, String corePath) throws FHIRException, IOException { 1268 StructureDefinitionRenderer sdr = new StructureDefinitionRenderer(new RenderingContext(utilsLeft.getContext(), null, utilsLeft.getTerminologyServiceOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER).setPkp(this)); 1269 return sdr.generateTable(new RenderingStatus(), corePath, comp.intersection, false, prefix, false, id, true, corePath, prefix, false, true, null, false, sdr.getContext().withUniqueLocalPrefix("i"), "i", null); 1270 } 1271 1272 private void genElementComp(String defPath, String anchorPrefix, HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<ElementDefinitionNode> combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { 1273 Row originalRow = slicingRow; 1274 Row typesRow = null; 1275 1276 List<StructuralMatch<ElementDefinitionNode>> children = combined.getChildren(); 1277 1278 Row row = gen.new Row(); 1279 rows.add(row); 1280 String path = combined.either().getDef().getPath(); 1281 row.setAnchor(path); 1282 row.setColor(utilsRight.getRowColor(combined.either().getDef(), false)); 1283 if (eitherHasSlicing(combined)) 1284 row.setLineColor(1); 1285 else if (eitherHasSliceName(combined)) 1286 row.setLineColor(2); 1287 else 1288 row.setLineColor(0); 1289 boolean ext = false; 1290 if (tail(path).equals("extension")) { 1291 if (elementIsComplex(combined)) 1292 row.setIcon("icon_extension_complex.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_COMPLEX)); 1293 else 1294 row.setIcon("icon_extension_simple.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 1295 ext = true; 1296 } else if (tail(path).equals("modifierExtension")) { 1297 if (elementIsComplex(combined)) 1298 row.setIcon("icon_modifier_extension_complex.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_COMPLEX)); 1299 else 1300 row.setIcon("icon_modifier_extension_simple.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 1301 } else if (hasChoice(combined)) { 1302 if (allAreReference(combined)) 1303 row.setIcon("icon_reference.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_REFERENCE)); 1304 else { 1305 row.setIcon("icon_choice.gif", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_CHOICE)); 1306 typesRow = row; 1307 } 1308 } else if (combined.either().getDef().hasContentReference()) 1309 row.setIcon("icon_reuse.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_REUSE)); 1310 else if (isPrimitive(combined)) 1311 row.setIcon("icon_primitive.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_PRIMITIVE)); 1312 else if (hasTarget(combined)) 1313 row.setIcon("icon_reference.png", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_REFERENCE)); 1314 else if (isDataType(combined)) 1315 row.setIcon("icon_datatype.gif", session.getI18n().formatPhrase(RenderingContext.TEXT_ICON_DATATYPE)); 1316 else 1317 row.setIcon("icon_resource.png", session.getI18n().formatPhrase(RenderingContext.GENERAL_RESOURCE)); 1318 String ref = defPath == null ? null : defPath + combined.either().getDef().getId(); 1319 String sName = tail(path); 1320 String sn = getSliceName(combined); 1321 if (sn != null) 1322 sName = sName +":"+sn; 1323 StructureDefinitionRenderer.UnusedTracker used = new StructureDefinitionRenderer.UnusedTracker(); 1324 StructureDefinitionRenderer sdrLeft = new StructureDefinitionRenderer(new RenderingContext(utilsLeft.getContext(), null, utilsLeft.getTerminologyServiceOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER).setPkp(this)); 1325 StructureDefinitionRenderer sdrRight= new StructureDefinitionRenderer(new RenderingContext(utilsRight.getContext(), null, utilsRight.getTerminologyServiceOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER).setPkp(this)); 1326 1327 1328 1329 Cell nc; 1330 String leftColor = !combined.hasLeft() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null; 1331 String rightColor = !combined.hasRight() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null; 1332 if (combined.hasLeft()) { 1333 nc = sdrLeft.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName, null); 1334 } else { 1335 nc = sdrRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName, null); 1336 } 1337 if (combined.hasLeft()) { 1338 frame(sdrLeft.genElementCells(new RenderingStatus(), gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false, sdrLeft.getContext(), children.size() > 0, defPath, anchorPrefix, new ArrayList<ElementDefinition>(), null), leftColor); 1339 } else { 1340 frame(spacers(row, 4, gen), leftColor); 1341 } 1342 if (combined.hasRight()) { 1343 frame(sdrRight.genElementCells(new RenderingStatus(), gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false, sdrRight.getContext(), children.size() > 0, defPath, anchorPrefix, new ArrayList<ElementDefinition>(), null), rightColor); 1344 } else { 1345 frame(spacers(row, 4, gen), rightColor); 1346 } 1347 row.getCells().add(cellForMessages(gen, combined.getMessages())); 1348 1349 for (StructuralMatch<ElementDefinitionNode> child : children) { 1350 genElementComp(defPath, anchorPrefix, gen, row.getSubRows(), child, corePath, prefix, originalRow, false); 1351 } 1352 } 1353 1354 private void frame(List<Cell> cells, String color) { 1355 for (Cell cell : cells) { 1356 if (color != null) { 1357 cell.setStyle("background-color: "+color); 1358 } 1359 } 1360 cells.get(0).setStyle("border-left: 1px grey solid"+(color == null ? "" : "; background-color: "+color)); 1361 cells.get(cells.size()-1).setStyle("border-right: 1px grey solid"+(color == null ? "" : "; background-color: "+color)); 1362 } 1363 1364 private List<Cell> spacers(Row row, int count, HierarchicalTableGenerator gen) { 1365 List<Cell> res = new ArrayList<>(); 1366 for (int i = 0; i < count; i++) { 1367 Cell c = gen.new Cell(); 1368 res.add(c); 1369 row.getCells().add(c); 1370 } 1371 return res; 1372 } 1373 1374 private String getSliceName(StructuralMatch<ElementDefinitionNode> combined) { 1375 // TODO Auto-generated method stub 1376 return null; 1377 } 1378 1379 private boolean isDataType(StructuralMatch<ElementDefinitionNode> combined) { 1380 // TODO Auto-generated method stub 1381 return false; 1382 } 1383 1384 private boolean hasTarget(StructuralMatch<ElementDefinitionNode> combined) { 1385 // TODO Auto-generated method stub 1386 return false; 1387 } 1388 1389 private boolean isPrimitive(StructuralMatch<ElementDefinitionNode> combined) { 1390 // TODO Auto-generated method stub 1391 return false; 1392 } 1393 1394 private boolean allAreReference(StructuralMatch<ElementDefinitionNode> combined) { 1395 // TODO Auto-generated method stub 1396 return false; 1397 } 1398 1399 private boolean hasChoice(StructuralMatch<ElementDefinitionNode> combined) { 1400 // TODO Auto-generated method stub 1401 return false; 1402 } 1403 1404 private boolean elementIsComplex(StructuralMatch<ElementDefinitionNode> combined) { 1405 // TODO Auto-generated method stub velement.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue() 1406 return false; 1407 } 1408 1409 private boolean eitherHasSliceName(StructuralMatch<ElementDefinitionNode> combined) { 1410 // TODO Auto-generated method stub 1411 return false; 1412 } 1413 1414 private boolean eitherHasSlicing(StructuralMatch<ElementDefinitionNode> combined) { 1415 // TODO Auto-generated method stub 1416 return false; 1417 } 1418 1419 1420 1421 1422private String tail(String path) { 1423 if (path.contains(".")) 1424 return path.substring(path.lastIndexOf('.')+1); 1425 else 1426 return path; 1427} 1428 1429@Override 1430public boolean isDatatype(String typeSimple) { 1431 // TODO Auto-generated method stub 1432 return false; 1433} 1434 1435@Override 1436public boolean isPrimitiveType(String typeSimple) { 1437 // TODO Auto-generated method stub 1438 return false; 1439} 1440 1441@Override 1442public boolean isResource(String typeSimple) { 1443// return false; 1444 throw new NotImplementedError(); 1445} 1446 1447@Override 1448public boolean hasLinkFor(String typeSimple) { 1449 return false; 1450} 1451 1452@Override 1453public String getLinkFor(String corePath, String typeSimple) { 1454 return "??|??"; 1455} 1456 1457@Override 1458public BindingResolution resolveBinding(StructureDefinition def, ElementDefinitionBindingComponent binding, String path) 1459 throws FHIRException { 1460 return new BindingResolution("??", "??"); 1461} 1462 1463@Override 1464public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException { 1465 return new BindingResolution("??", "??"); 1466} 1467 1468@Override 1469public String getLinkForProfile(StructureDefinition profile, String url) { 1470 return "??|??"; 1471} 1472 1473@Override 1474public boolean prependLinks() { 1475 return false; 1476} 1477 1478@Override 1479public String getLinkForUrl(String corePath, String s) { 1480 return null; 1481} 1482 1483@Override 1484public String getCanonicalForDefaultContext() { 1485 // TODO Auto-generated method stub 1486 return null; 1487} 1488 1489 1490}