001package org.hl7.fhir.r5.comparison; 002 003import java.io.IOException; 004import java.util.HashMap; 005import java.util.Map; 006import java.util.UUID; 007 008import org.hl7.fhir.exceptions.DefinitionException; 009import org.hl7.fhir.exceptions.FHIRException; 010import org.hl7.fhir.exceptions.FHIRFormatError; 011import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.CanonicalResourceComparison; 012import org.hl7.fhir.r5.comparison.CapabilityStatementComparer.CapabilityStatementComparison; 013import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison; 014import org.hl7.fhir.r5.comparison.ResourceComparer.ResourceComparison; 015import org.hl7.fhir.r5.comparison.StructureDefinitionComparer.ProfileComparison; 016import org.hl7.fhir.r5.comparison.ValueSetComparer.ValueSetComparison; 017import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation.AnotationType; 018import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; 019import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 020import org.hl7.fhir.r5.context.IWorkerContext; 021import org.hl7.fhir.r5.model.Base; 022import org.hl7.fhir.r5.model.CanonicalResource; 023import org.hl7.fhir.r5.model.CapabilityStatement; 024import org.hl7.fhir.r5.model.CodeSystem; 025import org.hl7.fhir.r5.model.Resource; 026import org.hl7.fhir.r5.model.StructureDefinition; 027import org.hl7.fhir.r5.model.ValueSet; 028 029public class ComparisonSession { 030 031 032 private Map<String, ResourceComparison> compares = new HashMap<>(); 033 private IWorkerContext contextLeft; 034 private IWorkerContext contextRight; 035 private String sessiondId; 036 private int count; 037 private boolean debug; 038 private boolean annotate; 039 private String title; 040 private ProfileKnowledgeProvider pkpLeft; 041 private ProfileKnowledgeProvider pkpRight; 042 043 public ComparisonSession(IWorkerContext contextLeft, IWorkerContext contextRight, String title, ProfileKnowledgeProvider pkpLeft, ProfileKnowledgeProvider pkpRight) { 044 super(); 045 this.contextLeft = contextLeft; 046 this.contextRight = contextRight; 047 this.sessiondId = UUID.randomUUID().toString().toLowerCase(); 048 this.title = title; 049 this.pkpLeft = pkpLeft; 050 this.pkpRight = pkpRight; 051 debug = false; 052 } 053 054 public IWorkerContext getContextLeft() { 055 return contextLeft; 056 } 057 058 public IWorkerContext getContextRight() { 059 return contextRight; 060 } 061 062 public String getTitle() { 063 return title; 064 } 065 066 public ResourceComparison compare(String left, Resource leftSource, String right, Resource rightSource) throws DefinitionException, FHIRFormatError, IOException { 067 CanonicalResource l = (CanonicalResource) contextLeft.fetchResource(Resource.class, left, leftSource); 068 if (l == null) { 069 throw new DefinitionException("Unable to resolve "+left); 070 } 071 CanonicalResource r = (CanonicalResource) contextRight.fetchResource(Resource.class, right, rightSource); 072 if (r == null) { 073 throw new DefinitionException("Unable to resolve "+right); 074 } 075 return compare(l, r); 076 } 077 078 public ResourceComparison compare(CanonicalResource left, CanonicalResource right) throws DefinitionException, FHIRFormatError, IOException { 079 if (left != null && right != null) { 080 String key = key(left.getUrl(), left.getVersion(), right.getUrl(), right.getVersion()); 081 if (compares.containsKey(key)) { 082 // if null then the comparison is in progress. 083 // this can happen when profiles refer to each other 084 return compares.get(key); 085 } 086 compares.put(key, null); 087 try { 088 if (left instanceof CodeSystem && right instanceof CodeSystem) { 089 CodeSystemComparer cs = new CodeSystemComparer(this); 090 CodeSystemComparison csc = cs.compare((CodeSystem) left, (CodeSystem) right); 091 compares.put(key, csc); 092 return csc; 093 } else if (left instanceof ValueSet && right instanceof ValueSet) { 094 ValueSetComparer cs = new ValueSetComparer(this); 095 ValueSetComparison csc = cs.compare((ValueSet) left, (ValueSet) right); 096 compares.put(key, csc); 097 return csc; 098 } else if (left instanceof StructureDefinition && right instanceof StructureDefinition) { 099 StructureDefinitionComparer cs = new StructureDefinitionComparer(this, new ProfileUtilities(contextLeft, null, pkpLeft), new ProfileUtilities(contextRight, null, pkpRight)); 100 ProfileComparison csc = cs.compare((StructureDefinition) left, (StructureDefinition) right); 101 compares.put(key, csc); 102 return csc; 103 } else if (left instanceof CapabilityStatement && right instanceof CapabilityStatement) { 104 CapabilityStatementComparer cs = new CapabilityStatementComparer(this); 105 CapabilityStatementComparison csc = cs.compare((CapabilityStatement) left, (CapabilityStatement) right); 106 compares.put(key, csc); 107 return csc; 108 } else { 109 throw new FHIRException("Unable to compare resources of type "+left.fhirType()+" and "+right.fhirType()); 110 } 111 } catch (Throwable e) { 112// if (debug) { 113 e.printStackTrace(); 114// } 115 ResourceComparer.PlaceHolderComparison csc = new ResourceComparer.PlaceHolderComparison(left, right, e); 116 compares.put(key, csc); 117 return csc; 118 } 119 } else if (left != null) { 120 markDeleted(null, left.fhirType(), left); // todo: waht? 121 String key = key(left.getUrl(), left.getVersion(), left.getUrl(), left.getVersion()); 122 if (compares.containsKey(key)) { 123 return compares.get(key); 124 } 125 ResourceComparer.PlaceHolderComparison csc = new ResourceComparer.PlaceHolderComparison(left, right); 126 compares.put(key, csc); 127 return csc; 128 } else { 129 markAdded(right); 130 String key = key(right.getUrl(), right.getVersion(), right.getUrl(), right.getVersion()); 131 if (compares.containsKey(key)) { 132 return compares.get(key); 133 } 134 ResourceComparer.PlaceHolderComparison csc = new ResourceComparer.PlaceHolderComparison(left, right); 135 compares.put(key, csc); 136 return csc; 137 } 138 } 139 140 private String key(String urlL, String verL, String urlR, String verR) { 141 return urlL+"|"+verL+"||"+urlR+"|"+verR; 142 } 143 144 public void identify(CanonicalResource res) { 145 count++; 146 res.setId(sessiondId+"-"+count); 147 res.setUrl("http://hl7.org/fhir/comparison/"+res.fhirType()+"/"+res.getId()); 148 } 149 150 public void identify(ResourceComparison res) { 151 count++; 152 } 153 154 public boolean isDebug() { 155 return debug; 156 } 157 158 public void setDebug(boolean debug) { 159 this.debug = debug; 160 } 161 162 public Map<String, ResourceComparison> getCompares() { 163 return compares; 164 } 165 166 public ProfileKnowledgeProvider getPkpLeft() { 167 return pkpLeft; 168 } 169 170 public ProfileKnowledgeProvider getPkpRight() { 171 return pkpRight; 172 } 173 174 public boolean isAnnotate() { 175 return annotate; 176 } 177 178 public void setAnnotate(boolean annotate) { 179 this.annotate = annotate; 180 } 181 182 private VersionComparisonAnnotation getAnnotation(Base b) { 183 if (b.hasUserData(VersionComparisonAnnotation.USER_DATA_NAME)) { 184 return (VersionComparisonAnnotation) b.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); 185 } else { 186 VersionComparisonAnnotation vca = new VersionComparisonAnnotation(AnotationType.NoChange); 187 b.setUserData(VersionComparisonAnnotation.USER_DATA_NAME, vca); 188 return vca; 189 } 190 } 191 192 public void markAdded(Base focus) { 193 if (isAnnotate()) { 194 getAnnotation(focus).added(); 195 } 196 } 197 198 public void markChanged(Base focus, Base original) { 199 if (isAnnotate()) { 200 getAnnotation(focus).changed(original); 201 } 202 } 203 204 public void markDeleted(Base parent, String name, Base other) { 205 if (isAnnotate() && other != null) { 206 getAnnotation(parent).deleted(name, other); 207 getAnnotation(other).deleted(); 208 } 209 } 210 211 public void annotate(Base base, CanonicalResourceComparison<? extends CanonicalResource> comp) { 212 if (isAnnotate()) { 213 getAnnotation(base).comp(comp); 214 } 215 } 216}