All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hl7.fhir.r5.comparison.CodeSystemComparer Maven / Gradle / Ivy

package org.hl7.fhir.r5.comparison;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.StructureDefinitionComparer.ProfileComparison;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer;
import org.hl7.fhir.r5.renderers.CodeSystemRenderer;
import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.RenderingI18nContext;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
import org.hl7.fhir.utilities.xhtml.XhtmlDocument;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;

@MarkedToMoveToAdjunctPackage
public class CodeSystemComparer extends CanonicalResourceComparer {


  public class CodeSystemComparison extends CanonicalResourceComparison {

    private StructuralMatch properties = new StructuralMatch(); 
    private StructuralMatch filters = new StructuralMatch();                                             
    private StructuralMatch combined;                                             
    private Map propMap = new HashMap<>(); // right to left; left retains it's name
    public CodeSystemComparison(CodeSystem left, CodeSystem right) {
      super(left, right);
      combined = new StructuralMatch(); // base
    }

    public Map getPropMap() {
      return propMap;
    }

    public StructuralMatch getCombined() {
      return combined;
    }

    public StructuralMatch getProperties() {
      return properties;
    }

    public StructuralMatch getFilters() {
      return filters;
    }

    @Override
    protected String abbreviation() {
      return "cs";
    }

    @Override
    protected String summary() {
      String res = "CodeSystem: "+left.present()+" vs "+right.present();
      String ch = changeSummary();
      if (ch != null) {
        res = res + ". "+ch;
      }
      return res;
    }


    @Override
    protected String fhirType() {
      return "CodeSystem";
    }

    @Override
    protected void countMessages(MessageCounts cnts) {
      super.countMessages(cnts);
      combined.countMessages(cnts);
    }

  }

  private CodeSystem right;

  public CodeSystemComparer(ComparisonSession session) {
    super(session);
  }

  public CodeSystemComparison compare(CodeSystem left, CodeSystem right) {    
    if (left == null)
      throw new DefinitionException("No CodeSystem provided (left)");
    if (right == null)
      throw new DefinitionException("No CodeSystem provided (right)");
    
    CodeSystemComparison res = new CodeSystemComparison(left, right);
    session.identify(res);
    CodeSystem cs = new CodeSystem();
    res.setUnion(cs);
    session.identify(cs);
    cs.setName("Union"+left.getName()+"And"+right.getName());
    cs.setTitle("Union of "+left.getTitle()+" And "+right.getTitle());
    cs.setStatus(left.getStatus());
    cs.setDate(new Date());
    for (PropertyComponent pL : left.getProperty()) {
      cs.addProperty(pL.copy());
    }
    for (PropertyComponent pR : left.getProperty()) {
      PropertyComponent pL = findProperty(left, pR);
      if (pL == null) {
        String code = getUniqued(pR.getCode(), cs.getProperty());
        cs.addProperty(pR.copy().setCode(code));
      } else {
        res.getPropMap().put(pR.getCode(), pL.getCode());
      }
    }

    CodeSystem cs1 = new CodeSystem();
    res.setIntersection(cs1);
    session.identify(cs1);
    cs1.setName("Intersection"+left.getName()+"And"+right.getName());
    cs1.setTitle("Intersection of "+left.getTitle()+" And "+right.getTitle());
    cs1.setStatus(left.getStatus());
    cs1.setDate(new Date());
    cs1.getProperty().addAll(cs.getProperty());


    List chMetadata = new ArrayList<>();
    boolean ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata, right);
    if (comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res)) {
      ch = true;
      chMetadata.add("versionNeeded");
    }
    if (comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
      ch = true;
      chMetadata.add("compositional");
    }
    res.updatedMetadataState(ch, chMetadata);
    ch = false;
    ch = comparePrimitivesWithTracking("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res, right) || ch;
    ch = comparePrimitivesWithTracking("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res, right) || ch;
    ch = comparePrimitivesWithTracking("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res, right);
    
    ch = compareProperties(left.getProperty(), right.getProperty(), res.getProperties(), res.getUnion().getProperty(), res.getIntersection().getProperty(), res.getUnion(), res.getIntersection(), res, "CodeSystem.property", right) || ch;
    ch = compareFilters(left.getFilter(), right.getFilter(), res.getFilters(), res.getUnion().getFilter(), res.getIntersection().getFilter(), res.getUnion(), res.getIntersection(), res, "CodeSystem.filter", right) || ch;
    ch = compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept", right) || ch;
    res.updateDefinitionsState(ch);

    session.annotate(right, res);
    return res;
  }

  private String getUniqued(String code, List list) {
    int i = 0;
    while (true) {
      boolean ok = true;
      String res = code+(i == 0 ? "" : i);
      for (PropertyComponent t : list) {
        if (res.equals(t.getCode())) {
          ok = false;
        }
      }
      if (ok) {
        return res;
      }
    }
  }

  private PropertyComponent findProperty(CodeSystem left, PropertyComponent p) {
    for (PropertyComponent t : left.getProperty()) {
      if (p.hasUri() && t.hasUri() && p.getUri().equals(t.getUri())) {
        return t;
      } else if (!p.hasUri() && !t.hasUri() && p.getCode().equals(t.getCode())) {
        return t;
      }
    }
    return null;
  }

  private boolean compareProperties(List left, List right, StructuralMatch combined,
    List union, List intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
    boolean def = false;
    List matchR = new ArrayList<>();
    for (PropertyComponent l : left) {
      PropertyComponent r = findInList(right, l);
      if (r == null) {
        union.add(l);
        res.updateContentState(true);
        combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
        session.markDeleted(parent, "concept", l);
      } else {
        matchR.add(r);
        PropertyComponent cdM = merge(l, r, res);
        PropertyComponent cdI = intersect(l, r, res);
        union.add(cdM);
        intersection.add(cdI);
        StructuralMatch sm = new StructuralMatch(l, r);
        if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
          def = true;
        }
        combined.getChildren().add(sm);
      }
    }
    for (PropertyComponent r : right) {
      if (!matchR.contains(r)) {
        union.add(r);
        res.updateContentState(true);
        combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));    
        session.markAdded(r);     
      }
    }
    return def;
  }


  private boolean compareFilters(List left, List right, StructuralMatch combined,
    List union, List intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
    boolean def = false;
    List matchR = new ArrayList<>();
    for (CodeSystemFilterComponent l : left) {
      CodeSystemFilterComponent r = findInList(right, l);
      if (r == null) {
        union.add(l);
        res.updateContentState(true);
        combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
        session.markDeleted(parent, "concept", l);
      } else {
        matchR.add(r);
        CodeSystemFilterComponent cdM = merge(l, r, res);
        CodeSystemFilterComponent cdI = intersect(l, r, res);
        union.add(cdM);
        intersection.add(cdI);
        StructuralMatch sm = new StructuralMatch(l, r);
        if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
          def = true;
        }
        combined.getChildren().add(sm);
      }
    }
    for (CodeSystemFilterComponent r : right) {
      if (!matchR.contains(r)) {
        union.add(r);
        res.updateContentState(true);
        combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));    
        session.markAdded(r);     
      }
    }
    return def;
  }

  
  private boolean compareConcepts(List left, List right, StructuralMatch combined,
    List union, List intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
    boolean def = false;
    List matchR = new ArrayList<>();
    for (ConceptDefinitionComponent l : left) {
      ConceptDefinitionComponent r = findInList(right, l);
      if (r == null) {
        union.add(l);
        res.updateContentState(true);
        combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
        session.markDeleted(parent, "concept", l);
      } else {
        matchR.add(r);
        ConceptDefinitionComponent cdM = merge(l, r, csU.getProperty(), res);
        ConceptDefinitionComponent cdI = intersect(l, r, res);
        union.add(cdM);
        intersection.add(cdI);
        StructuralMatch sm = new StructuralMatch(l, r);
        if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
          def = true;
        }
        combined.getChildren().add(sm);
        if (compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept", r)) {
          def = true;
        }
      }
    }
    for (ConceptDefinitionComponent r : right) {
      if (!matchR.contains(r)) {
        union.add(r);
        res.updateContentState(true);
        combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));    
        session.markAdded(r);     
      }
    }
    return def;
  }
  
  private CodeSystemFilterComponent findInList(List list, CodeSystemFilterComponent item) {
    for (CodeSystemFilterComponent t : list) {
      if (t.getCode().equals(item.getCode())) {
        return t;
      }
    }
    return null;
  }


  private ConceptDefinitionComponent findInList(List list, ConceptDefinitionComponent item) {
    for (ConceptDefinitionComponent t : list) {
      if (t.getCode().equals(item.getCode())) {
        return t;
      }
    }
    return null;
  }


  private PropertyComponent findInList(List list, PropertyComponent item) {
    for (PropertyComponent t : list) {
      if (t.getCode().equals(item.getCode())) {
        return t;
      }
    }
    return null;
  }

  private boolean compare(List msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res, Base parent) {
    boolean result = false;
    result = compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res, parent, l.getDisplayElement(), r.getDisplayElement()) || result;
    result = compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res, parent, l.getDefinitionElement(), r.getDefinitionElement()) || result;
    // todo: designations, properties
    return result;
  }

  private boolean compare(List msgs, PropertyComponent l, PropertyComponent r, String path, CodeSystemComparison res, Base parent) {
    boolean result = false;
    result = compareStrings(path, msgs, l.getUri(), r.getUri(), "uri", IssueSeverity.WARNING, res, parent, l.getUriElement(), r.getUriElement()) || result;
    result = compareStrings(path, msgs, l.hasType() ? l.getType().toCode() : null, r.hasType() ? r.getType().toCode() : null, "type", IssueSeverity.ERROR, res, parent, l.getTypeElement(), r.getTypeElement()) || result;
    result = compareStrings(path, msgs, l.getDescription(), r.getDescription(), "description", IssueSeverity.WARNING, res, parent, l.getDescriptionElement(), r.getDescriptionElement()) || result;
    return result;
  }

  private boolean compare(List msgs, CodeSystemFilterComponent l, CodeSystemFilterComponent r, String path, CodeSystemComparison res, Base parent) {
    boolean result = false;
    result = compareStrings(path, msgs, l.getDescription(), r.getDescription(), "description", IssueSeverity.WARNING, res, parent, l.getDescriptionElement(), r.getDescriptionElement()) || result;
// todo: repeating
//    result = compareStrings(path, msgs, l.hasOperator() ? l.getOperator().toCode() : null, r.hasType() ? r.getType().toCode() : null, "type", IssueSeverity.ERROR, res, parent, l.getTypeElement(), r.getTypeElement()) || result;
    result = compareStrings(path, msgs, l.getValue(), r.getValue(), "value", IssueSeverity.WARNING, res, parent, l.getValueElement(), r.getValueElement()) || result;
    return result;
  }

  private boolean compareStrings(String path, List msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res, Base parent, Base l, Base r) {
    if (!Utilities.noString(right)) {
      if (Utilities.noString(left)) {
        msgs.add(vmI(level, "Value for "+name+" added", path));   
        session.markAdded(r);     
        return true;
      } else if (!left.equals(right)) {
        if (level != IssueSeverity.NULL) {
          res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+left+"' vs '"+right+"'", level));
        }
        msgs.add(vmI(level, name+" changed from left to right", path));
        session.markChanged(r, l);   
        return true;
      }
    } else if (!Utilities.noString(left)) {
      msgs.add(vmI(level, "Value for "+name+" removed", path));
      session.markDeleted(parent, "concept", l);
      return true;
    }
    return false;
  }

  private ConceptDefinitionComponent merge(ConceptDefinitionComponent l, ConceptDefinitionComponent r, List destProps, CodeSystemComparison res) {
    ConceptDefinitionComponent cd = l.copy();
    if (!l.hasDisplay() && r.hasDisplay()) {
      cd.setDisplay(r.getDisplay());
    }
    if (!l.hasDefinition() && r.hasDefinition()) {
      cd.setDefinition(r.getDefinition());
    }
    mergeProps(cd, l, r, destProps, res);
    mergeDesignations(cd, l, r);
    return cd;
  }

  private PropertyComponent merge(PropertyComponent l, PropertyComponent r, CodeSystemComparison res) {
    PropertyComponent cd = l.copy();
    if (!l.hasDescription() && r.hasDescription()) {
      cd.setDescription(r.getDescription());
    }
    return cd;
  }


  private CodeSystemFilterComponent merge(CodeSystemFilterComponent l, CodeSystemFilterComponent r, CodeSystemComparison res) {
    CodeSystemFilterComponent cd = l.copy();
    if (!l.hasDescription() && r.hasDescription()) {
      cd.setDescription(r.getDescription());
    }
    return cd;
  }

  private ConceptDefinitionComponent intersect(ConceptDefinitionComponent l, ConceptDefinitionComponent r, CodeSystemComparison res) {
    ConceptDefinitionComponent cd = l.copy();
    if (l.hasDisplay() && !r.hasDisplay()) {
      cd.setDisplay(null);
    }
    if (l.hasDefinition() && !r.hasDefinition()) {
      cd.setDefinition(null);
    }
    intersectProps(cd, l, r, res);
    //    mergeDesignations(cd, l, r);
    return cd;
  }

  private PropertyComponent intersect(PropertyComponent l, PropertyComponent r, CodeSystemComparison res) {
    PropertyComponent cd = l.copy();
    if (l.hasDescription() && !r.hasDescription()) {
      cd.setDescription(null);
    }
    return cd;
  }

  private CodeSystemFilterComponent intersect(CodeSystemFilterComponent l, CodeSystemFilterComponent r, CodeSystemComparison res) {
    CodeSystemFilterComponent cd = l.copy();
    if (l.hasDescription() && !r.hasDescription()) {
      cd.setDescription(null);
    }
    return cd;
  }

  private void mergeDesignations(ConceptDefinitionComponent cd, ConceptDefinitionComponent l, ConceptDefinitionComponent r) {
    for (ConceptDefinitionDesignationComponent td : l.getDesignation()) {
      if (hasDesignation(td, r.getDesignation())) {
        cd.getDesignation().add(td);
      }
    }
    for (ConceptDefinitionDesignationComponent td : r.getDesignation()) {
      if (hasDesignation(td, l.getDesignation())) {
        cd.getDesignation().add(td);
      }
    }
  }

  private boolean hasDesignation(ConceptDefinitionDesignationComponent td, List designation) {
    for (ConceptDefinitionDesignationComponent t : designation) {
      if (designationsMatch(td, t)) {
        return true;
      }
    }
    return false;
  }

  private boolean designationsMatch(ConceptDefinitionDesignationComponent l, ConceptDefinitionDesignationComponent r) {
    if (l.hasUse() != r.hasUse()) {
      return false;
    }
    if (l.hasLanguage() != r.hasLanguage()) {
      return false;
    }
    if (l.hasValue() != r.hasValue()) {
      return false;
    }
    if (l.hasUse()) {
      if (l.getUse().equalsDeep(r.getUse())) {
        return false;
      }
    }
    if (l.hasLanguage()) {
      if (l.getLanguageElement().equalsDeep(r.getLanguageElement())) {
        return false;
      }
    }
    if (l.hasValue()) {
      if (l.getValueElement().equalsDeep(r.getValueElement())) {
        return false;
      }
    }
    return true;
  }

  private void mergeProps(ConceptDefinitionComponent cd, ConceptDefinitionComponent l, ConceptDefinitionComponent r, List destProps, CodeSystemComparison res) {
    List matchR = new ArrayList<>();
    for (ConceptPropertyComponent lp : l.getProperty()) {
      ConceptPropertyComponent rp = findRightProp(r.getProperty(), lp, res);
      if (rp == null) {
        cd.getProperty().add(lp);
      } else {
        matchR.add(rp);
        cd.getProperty().add(lp);
        if (lp.getValue().equalsDeep(rp.getValue())) {
          cd.getProperty().add(rp.setCode(res.getPropMap().get(rp.getCode())));
        }
      }
    }
    for (ConceptPropertyComponent rp : r.getProperty()) {
      if (!matchR.contains(rp)) {
        cd.getProperty().add(rp.setCode(res.getPropMap().get(rp.getCode())));        
      }
    }
  }

  private void intersectProps(ConceptDefinitionComponent cd, ConceptDefinitionComponent l, ConceptDefinitionComponent r, CodeSystemComparison res) {
    for (ConceptPropertyComponent lp : l.getProperty()) {
      ConceptPropertyComponent rp = findRightProp(r.getProperty(), lp, res);
      if (rp != null) {
        cd.getProperty().add(lp);
      }
    }
  }

  private ConceptPropertyComponent findRightProp(List rightProperties, ConceptPropertyComponent lp, CodeSystemComparison res) {
    for (ConceptPropertyComponent p : rightProperties) {
      String rp = res.getPropMap().get(p.getCode());
      if (rp != null && rp.equals(lp.getCode())) {
        return p;
      }
    }
    return null;
  }

  public XhtmlNode renderConcepts(CodeSystemComparison comparison, String id, String prefix) throws FHIRException, IOException {
    // columns: code, display (left|right), properties (left|right)
    HierarchicalTableGenerator gen = new HierarchicalTableGenerator(new RenderingI18nContext(), Utilities.path("[tmp]", "compare"), false, "c");
    TableModel model = gen.new TableModel(id, true);
    model.setAlternating(true);
    model.getTitles().add(gen.new Title(null, null, "Code", "The code for the concept", null, 100));
    model.getTitles().add(gen.new Title(null, null, "Display", "The display for the concept", null, 200, 2));
    for (PropertyComponent p : comparison.getUnion().getProperty()) {
      model.getTitles().add(gen.new Title(null, null, p.getCode(), p.getDescription(), null, 100, 2));
    }
    model.getTitles().add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200));
    for (StructuralMatch t : comparison.getCombined().getChildren()) {
      addRow(gen, model.getRows(), t, comparison);
    }
    return gen.generate(model, prefix, 0, null);
  }

  private void addRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CodeSystemComparison comparison) {
    Row r = gen.new Row();
    rows.add(r);
    r.getCells().add(gen.new Cell(null, null, t.either().getCode(), null, null));
    if (t.hasLeft() && t.hasRight()) {
      if (t.getLeft().hasDisplay() && t.getRight().hasDisplay()) {
        if (t.getLeft().getDisplay().equals(t.getRight().getDisplay())) {
          r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null).span(2));        
        } else {
          r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));        
          r.getCells().add(gen.new Cell(null, null, t.getRight().getDisplay(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
        }
      } else if (t.getLeft().hasDisplay()) {
        r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null));        
        r.getCells().add(missingCell(gen, COLOR_NO_CELL_RIGHT));        
      } else if (t.getRight().hasDisplay()) {        
        r.getCells().add(missingCell(gen, COLOR_NO_CELL_LEFT));        
        r.getCells().add(gen.new Cell(null, null, t.getRight().getDisplay(), null, null));        
      } else {
        r.getCells().add(missingCell(gen).span(2));
      }
      for (PropertyComponent p : comparison.getUnion().getProperty()) {
        ConceptPropertyComponent lp = getProp(t.getLeft(), p, false, comparison);
        ConceptPropertyComponent rp = getProp(t.getRight(), p, true, comparison);

        if (lp != null && rp != null) {
          if (lp.getValue().equals(rp.getValue())) {
            r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null).span(2));        
          } else {
            r.getCells().add(gen.new Cell(null, null, lp.getValue().toString(), null, null));        
            r.getCells().add(gen.new Cell(null, null, rp.getValue().toString(), null, null));
          }
        } else if (lp != null) {
          r.getCells().add(gen.new Cell(null, null, lp.getValue().toString(), null, null));        
          r.getCells().add(missingCell(gen, COLOR_NO_CELL_RIGHT));        
        } else if (rp != null) {        
          r.getCells().add(missingCell(gen, COLOR_NO_CELL_LEFT));        
          r.getCells().add(gen.new Cell(null, null, rp.getValue().toString(), null, null));        
        } else {
          r.getCells().add(missingCell(gen).span(2));
        }

      }
    } else if (t.hasLeft()) {
      r.setColor(COLOR_NO_ROW_RIGHT);
      r.getCells().add(gen.new Cell(null, null, t.either().getDisplay(), null, null));
      r.getCells().add(missingCell(gen));
      for (PropertyComponent p : comparison.getUnion().getProperty()) {
        r.getCells().add(propertyCell(gen, t.getLeft(), p, false, comparison));
        r.getCells().add(missingCell(gen));
      }
    } else {
      r.setColor(COLOR_NO_ROW_LEFT);
      r.getCells().add(missingCell(gen));
      r.getCells().add(gen.new Cell(null, null, t.either().getDisplay(), null, null));
      for (PropertyComponent p : comparison.getUnion().getProperty()) {
        r.getCells().add(missingCell(gen));
        r.getCells().add(propertyCell(gen, t.getLeft(), p, true, comparison));
      }
    }
    r.getCells().add(cellForMessages(gen, t.getMessages()));
  }

  private Cell propertyCell(HierarchicalTableGenerator gen, ConceptDefinitionComponent cd, PropertyComponent p, boolean right, CodeSystemComparison comp) {
    ConceptPropertyComponent cp = getProp(cd, p, right, comp);
    if (cp == null) {
      return missingCell(gen, right ? COLOR_NO_CELL_RIGHT : COLOR_NO_CELL_LEFT);
    } else {
      return gen.new Cell(null, null, cp.getValue().toString(), null, null);
    }
  }

  public ConceptPropertyComponent getProp(ConceptDefinitionComponent cd, PropertyComponent p, boolean right, CodeSystemComparison comp) {
    String c = p.getCode();
    if (right) {
      c = comp.getPropMap().get(c);
    }
    ConceptPropertyComponent cp = null;
    if (cd != null) {
      for (ConceptPropertyComponent t : cd.getProperty()) {
        if (t.hasCode() && t.getCode().equals(c)) {
          cp = t;
        }
      }
    }
    return cp;
  }

  @Override
  protected String fhirType() {
    return "CodeSystem";
  }

  public XhtmlNode renderUnion(CodeSystemComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
    CodeSystemRenderer csr = new CodeSystemRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER));
    return csr.buildNarrative(ResourceWrapper.forResource(csr.getContext(), comp.union));
  }

  public XhtmlNode renderIntersection(CodeSystemComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
    CodeSystemRenderer csr = new CodeSystemRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER));
    return csr.buildNarrative(ResourceWrapper.forResource(csr.getContext(), comp.intersection));
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy