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}