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

org.hl7.fhir.r4b.comparison.CanonicalResourceComparer Maven / Gradle / Ivy

The newest version!
package org.hl7.fhir.r4b.comparison;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4b.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r4b.model.CanonicalResource;
import org.hl7.fhir.r4b.model.CanonicalType;
import org.hl7.fhir.r4b.model.CapabilityStatement;
import org.hl7.fhir.r4b.model.CodeType;
import org.hl7.fhir.r4b.model.CodeableConcept;
import org.hl7.fhir.r4b.model.Coding;
import org.hl7.fhir.r4b.model.PrimitiveType;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
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.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;

public abstract class CanonicalResourceComparer extends ResourceComparer {

  public abstract class CanonicalResourceComparison extends ResourceComparison {
    protected T left;
    protected T right;
    protected T union;
    protected T intersection;
    protected Map> metadata = new HashMap<>();

    public CanonicalResourceComparison(T left, T right) {
      super(left.getId(), right.getId());
      this.left = left;
      this.right = right;
    }

    public T getLeft() {
      return left;
    }

    public T getRight() {
      return right;
    }

    public T getUnion() {
      return union;
    }

    public T getIntersection() {
      return intersection;
    }

    public Map> getMetadata() {
      return metadata;
    }

    public void setLeft(T left) {
      this.left = left;
    }

    public void setRight(T right) {
      this.right = right;
    }

    public void setUnion(T union) {
      this.union = union;
    }

    public void setIntersection(T intersection) {
      this.intersection = intersection;
    }

    @Override
    protected String toTable() {
      String s = "";
      s = s + refCell(left);
      s = s + refCell(right);
      s = s + "Comparison";
      s = s + "" + outcomeSummary() + "";
      return "" + s + "\r\n";
    }

    @Override
    protected void countMessages(MessageCounts cnts) {
      for (StructuralMatch sm : metadata.values()) {
        sm.countMessages(cnts);
      }
    }
  }

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

  protected void compareMetadata(CanonicalResource left, CanonicalResource right,
      Map> comp, CanonicalResourceComparison res) {
    comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res);
    comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res);
    comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res);
    comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res);
    comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION,
        res);
    comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp,
        IssueSeverity.WARNING, res);
    comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res);
    comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp,
        IssueSeverity.INFORMATION, res);
    comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp,
        IssueSeverity.NULL, res);
    comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res);
    comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp,
        IssueSeverity.INFORMATION, res);
    compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp,
        IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction());
  }

  protected void compareCodeableConceptList(String name, List left, List right,
      Map> comp, IssueSeverity level,
      CanonicalResourceComparison res, List union,
      List intersection) {
    List matchR = new ArrayList<>();
    StructuralMatch combined = new StructuralMatch();
    for (CodeableConcept l : left) {
      CodeableConcept r = findCodeableConceptInList(right, l);
      if (r == null) {
        union.add(l);
        combined.getChildren().add(new StructuralMatch(gen(l), vm(IssueSeverity.INFORMATION,
            "Removed the item '" + gen(l) + "'", fhirType() + "." + name, res.getMessages())));
      } else {
        matchR.add(r);
        union.add(r);
        intersection.add(r);
        StructuralMatch sm = new StructuralMatch(gen(l), gen(r));
        combined.getChildren().add(sm);
      }
    }
    for (CodeableConcept r : right) {
      if (!matchR.contains(r)) {
        union.add(r);
        combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION,
            "Added the item '" + gen(r) + "'", fhirType() + "." + name, res.getMessages()), gen(r)));
      }
    }
    comp.put(name, combined);
  }

  private CodeableConcept findCodeableConceptInList(List list, CodeableConcept item) {
    for (CodeableConcept t : list) {
      if (t.matches(item)) {
        return t;
      }
    }
    return null;
  }

  protected String gen(CodeableConcept cc) {
    CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
    for (Coding c : cc.getCoding()) {
      b.append(gen(c));
    }
    return b.toString();
  }

  protected String gen(Coding c) {
    return c.getSystem() + (c.hasVersion() ? "|" + c.getVersion() : "") + "#" + c.getCode();
  }

  protected void compareCanonicalList(String name, List left, List right,
      Map> comp, IssueSeverity level,
      CanonicalResourceComparison res, List union,
      List intersection) {
    List matchR = new ArrayList<>();
    StructuralMatch combined = new StructuralMatch();
    for (CanonicalType l : left) {
      CanonicalType r = findCanonicalInList(right, l);
      if (r == null) {
        union.add(l);
        combined.getChildren().add(new StructuralMatch(l.getValue(), vm(IssueSeverity.INFORMATION,
            "Removed the item '" + l.getValue() + "'", fhirType() + "." + name, res.getMessages())));
      } else {
        matchR.add(r);
        union.add(r);
        intersection.add(r);
        StructuralMatch sm = new StructuralMatch(l.getValue(), r.getValue());
        combined.getChildren().add(sm);
      }
    }
    for (CanonicalType r : right) {
      if (!matchR.contains(r)) {
        union.add(r);
        combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION,
            "Added the item '" + r.getValue() + "'", fhirType() + "." + name, res.getMessages()), r.getValue()));
      }
    }
    comp.put(name, combined);
  }

  private CanonicalType findCanonicalInList(List list, CanonicalType item) {
    for (CanonicalType t : list) {
      if (t.getValue().equals(item.getValue())) {
        return t;
      }
    }
    return null;
  }

  protected void compareCodeList(String name, List left, List right,
      Map> comp, IssueSeverity level,
      CanonicalResourceComparison res, List union, List intersection) {
    List matchR = new ArrayList<>();
    StructuralMatch combined = new StructuralMatch();
    for (CodeType l : left) {
      CodeType r = findCodeInList(right, l);
      if (r == null) {
        union.add(l);
        combined.getChildren().add(new StructuralMatch(l.getValue(), vm(IssueSeverity.INFORMATION,
            "Removed the item '" + l.getValue() + "'", fhirType() + "." + name, res.getMessages())));
      } else {
        matchR.add(r);
        union.add(r);
        intersection.add(r);
        StructuralMatch sm = new StructuralMatch(l.getValue(), r.getValue());
        combined.getChildren().add(sm);
      }
    }
    for (CodeType r : right) {
      if (!matchR.contains(r)) {
        union.add(r);
        combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION,
            "Added the item '" + r.getValue() + "'", fhirType() + "." + name, res.getMessages()), r.getValue()));
      }
    }
    comp.put(name, combined);
  }

  private CodeType findCodeInList(List list, CodeType item) {
    for (CodeType t : list) {
      if (t.getValue().equals(item.getValue())) {
        return t;
      }
    }
    return null;
  }

  @SuppressWarnings("rawtypes")
  protected void comparePrimitives(String name, PrimitiveType l, PrimitiveType r,
      Map> comp, IssueSeverity level,
      CanonicalResourceComparison res) {
    StructuralMatch match = null;
    if (l.isEmpty() && r.isEmpty()) {
      match = new StructuralMatch<>(null, null, null);
    } else if (l.isEmpty()) {
      match = new StructuralMatch<>(null, r.primitiveValue(),
          vmI(IssueSeverity.INFORMATION, "Added the item '" + r.primitiveValue() + "'", fhirType() + "." + name));
    } else if (r.isEmpty()) {
      match = new StructuralMatch<>(l.primitiveValue(), null,
          vmI(IssueSeverity.INFORMATION, "Removed the item '" + l.primitiveValue() + "'", fhirType() + "." + name));
    } else if (!l.hasValue() && !r.hasValue()) {
      match = new StructuralMatch<>(null, null, vmI(IssueSeverity.INFORMATION, "No Value", fhirType() + "." + name));
    } else if (!l.hasValue()) {
      match = new StructuralMatch<>(null, r.primitiveValue(),
          vmI(IssueSeverity.INFORMATION, "No Value on Left", fhirType() + "." + name));
    } else if (!r.hasValue()) {
      match = new StructuralMatch<>(l.primitiveValue(), null,
          vmI(IssueSeverity.INFORMATION, "No Value on Right", fhirType() + "." + name));
    } else if (l.getValue().equals(r.getValue())) {
      match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(), null);
    } else {
      match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(),
          vmI(level, "Values Differ", fhirType() + "." + name));
      if (level != IssueSeverity.NULL) {
        res.getMessages()
            .add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, fhirType() + "." + name,
                "Values for " + name + " differ: '" + l.primitiveValue() + "' vs '" + r.primitiveValue() + "'", level));
      }
    }
    comp.put(name, match);
  }

  protected abstract String fhirType();

  public XhtmlNode renderMetadata(CanonicalResourceComparison 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);
    TableModel model = gen.new TableModel(id, true);
    model.setAlternating(true);
    model.getTitles().add(gen.new Title(null, null, "Name", "Property Name", null, 100));
    model.getTitles().add(gen.new Title(null, null, "Value", "The value of the property", null, 200, 2));
    model.getTitles()
        .add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200));

    for (String n : sorted(comparison.getMetadata().keySet())) {
      StructuralMatch t = comparison.getMetadata().get(n);
      addRow(gen, model.getRows(), n, t);
    }
    return gen.generate(model, prefix, 0, null);
  }

  private void addRow(HierarchicalTableGenerator gen, List rows, String name, StructuralMatch t) {
    Row r = gen.new Row();
    rows.add(r);
    r.getCells().add(gen.new Cell(null, null, name, null, null));
    if (t.hasLeft() && t.hasRight()) {
      if (t.getLeft().equals(t.getRight())) {
        r.getCells().add(gen.new Cell(null, null, t.getLeft(), null, null).span(2));
      } else {
        r.getCells()
            .add(gen.new Cell(null, null, t.getLeft(), null, null).setStyle("background-color: " + COLOR_DIFFERENT));
        r.getCells()
            .add(gen.new Cell(null, null, t.getRight(), null, null).setStyle("background-color: " + COLOR_DIFFERENT));
      }
    } else if (t.hasLeft()) {
      r.setColor(COLOR_NO_ROW_RIGHT);
      r.getCells().add(gen.new Cell(null, null, t.getLeft(), null, null));
      r.getCells().add(missingCell(gen));
    } else if (t.hasRight()) {
      r.setColor(COLOR_NO_ROW_LEFT);
      r.getCells().add(missingCell(gen));
      r.getCells().add(gen.new Cell(null, null, t.getRight(), null, null));
    } else {
      r.getCells().add(missingCell(gen).span(2));
    }
    r.getCells().add(cellForMessages(gen, t.getMessages()));
    int i = 0;
    for (StructuralMatch c : t.getChildren()) {
      addRow(gen, r.getSubRows(), name + "[" + i + "]", c);
      i++;
    }
  }

  private List sorted(Set keys) {
    List res = new ArrayList<>();
    res.addAll(keys);
    Collections.sort(res);
    return res;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy