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