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