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}