From ced57a5dfedac0d1fb55d7c45ba0202b9a61dfbc Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 18 Aug 2020 23:27:02 +1000 Subject: [PATCH] minor fixes to support main build (#311) * fix up POMs * fixes for main build to use packages * more config for validator * more options for validation version comparison * clear notes * more work on better presentation for comparison * Remove old comparison code * remove old comparison code * fix loading bug * fix to not load Simplifier packages via lazy loading --- .../comparison/CanonicalResourceComparer.java | 11 +- .../r5/comparison/ComparisonRenderer.java | 11 +- .../fhir/r5/comparison/ResourceComparer.java | 28 +- .../CapabilityStatementUtilities.java | 917 --------- .../fhir/r5/conformance/ProfileComparer.java | 1638 ----------------- .../fhir/r5/conformance/ProfileUtilities.java | 12 +- .../fhir/r5/context/SimpleWorkerContext.java | 2 +- .../renderers/DiagnosticReportRenderer.java | 3 +- .../org/hl7/fhir/r5/utils/KeyGenerator.java | 75 - .../hl7/fhir/utilities/cache/NpmPackage.java | 5 +- .../fhir/utilities/cache/PackageHacker.java | 11 +- .../cli/services/ComparisonService.java | 60 +- .../instance/InstanceValidator.java | 4 +- 13 files changed, 83 insertions(+), 2694 deletions(-) delete mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileComparer.java delete mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/KeyGenerator.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java index d3d80cd24..da811905f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java @@ -82,14 +82,9 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { @Override protected String toTable() { - String s = null; - if (left != null && right != null && !left.getUrl().equals(right.getUrl())) { - s = ""+left.getUrl()+""+right.getUrl()+""; - } else if (left != null) { - s = ""+left.getUrl()+""; - } else { - s = ""+right.getUrl()+""; - } + String s = ""; + s = s + refCell(left); + s = s + refCell(right); s = s + "Comparison"; s = s + ""+outcomeSummary()+""; return ""+s+"\r\n"; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java index b077b44b7..ceb3a3f75 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java @@ -56,10 +56,17 @@ public class ComparisonRenderer implements IEvaluationContext { return templates; } - public void render() throws IOException { + public void render(String leftName, String rightName) throws IOException { dumpBinaries(); StringBuilder b = new StringBuilder(); b.append("\r\n"); + b.append(" \r\n"); + b.append(" \r\n"); + b.append(" \r\n"); + b.append(" \r\n"); + b.append(" \r\n"); + b.append(" \r\n"); + List list = sorted(session.getCompares().keySet()); processList(list, b, "CodeSystem"); processList(list, b, "ValueSet"); @@ -82,7 +89,7 @@ public class ComparisonRenderer implements IEvaluationContext { if (comp.fhirType().equals(name)) { if (first) { first = false; - b.append("\r\n"); + b.append("\r\n"); } try { renderComparison(id, comp); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java index ecb17a159..92150276d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java @@ -60,6 +60,21 @@ public class ResourceComparer { id = abbreviation()+"-"+leftId + "-" + rightId; } + protected String refCell(CanonicalResource cr) { + if (cr == null) { + return ""; + } + String t = cr.present(); + if (Utilities.noString(t)) { + t = cr.getId(); + } + if (cr.hasUserData("path")) { + String p = cr.getUserString("path"); + return ""; + } else + return ""; + } + protected abstract String abbreviation(); public String getLeftId() { @@ -175,15 +190,10 @@ public class ResourceComparer { @Override protected String toTable() { - String s = null; + String s = ""; String color = null; - if (left != null && right != null && !left.getUrl().equals(right.getUrl())) { - s = ""; - } else if (left != null) { - s = ""; - } else { - s = ""; - } + s = s + refCell(left); + s = s + refCell(right); if (left == null) { s = s + ""; color = COLOR_NO_ROW_LEFT; @@ -198,6 +208,8 @@ public class ResourceComparer { return ""+s+"\r\n"; } + + public Throwable getE() { return e; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/CapabilityStatementUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/CapabilityStatementUtilities.java index 57d1edee7..903cb3e8e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/CapabilityStatementUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/CapabilityStatementUtilities.java @@ -1,923 +1,6 @@ package org.hl7.fhir.r5.conformance; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import org.hl7.fhir.exceptions.DefinitionException; -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r5.conformance.ProfileComparer.ProfileComparison; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.context.SimpleWorkerContext; -import org.hl7.fhir.r5.formats.IParser.OutputStyle; -import org.hl7.fhir.r5.formats.JsonParser; -import org.hl7.fhir.r5.formats.XmlParser; -import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.CanonicalType; -import org.hl7.fhir.r5.model.CapabilityStatement; -import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; -import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; -import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; -import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; -import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestSecurityComponent; -import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent; -import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent; -import org.hl7.fhir.r5.model.CodeableConcept; -import org.hl7.fhir.r5.model.Coding; -import org.hl7.fhir.r5.model.DomainResource; -import org.hl7.fhir.r5.model.Element; -import org.hl7.fhir.r5.model.Enumeration; -import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.Enumerations.RestfulCapabilityMode; -import org.hl7.fhir.r5.model.Extension; -import org.hl7.fhir.r5.model.OperationOutcome; -import org.hl7.fhir.r5.model.Property; -import org.hl7.fhir.r5.model.StringType; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.r5.utils.KeyGenerator; -import org.hl7.fhir.r5.utils.OperationOutcomeUtilities; -import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.MarkDownProcessor; -import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; -import org.hl7.fhir.utilities.TextFile; -import org.hl7.fhir.utilities.Utilities; -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.NodeType; -import org.hl7.fhir.utilities.xhtml.XhtmlComposer; -import org.hl7.fhir.utilities.xhtml.XhtmlDocument; -import org.hl7.fhir.utilities.xhtml.XhtmlNode; -import org.hl7.fhir.utilities.xhtml.XhtmlParser; - public class CapabilityStatementUtilities { - public class CapabilityStatementComparisonOutput { - public CapabilityStatementComparisonOutput() { - subset = new CapabilityStatement(); - keygen.genId(subset); - subset.setDate(new Date()); - subset.setName("intersection of "+selfName+" and "+otherName); - subset.setStatus(PublicationStatus.DRAFT); - - superset = new CapabilityStatement(); - keygen.genId(superset); - superset.setDate(subset.getDate()); - superset.setName("union of "+selfName+" and "+otherName); - superset.setStatus(PublicationStatus.DRAFT); - } - private CapabilityStatement superset; - private CapabilityStatement subset; - private OperationOutcome outcome; - private List messages = new ArrayList<>(); - public CapabilityStatement getSuperset() { - return superset; - } - public CapabilityStatement getSubset() { - return subset; - } - public OperationOutcome getOutcome() { - return outcome; - } - public List getMessages() { - return messages; - } - } - - private IWorkerContext context; - private String selfName; - private String otherName; - private CapabilityStatementComparisonOutput output; - private XhtmlDocument html; - private MarkDownProcessor markdown = new MarkDownProcessor(Dialect.COMMON_MARK); - private String folder; - private KeyGenerator keygen; -// private String css; - - public CapabilityStatementUtilities(SimpleWorkerContext context, String folder, KeyGenerator keygen) throws IOException { - super(); - this.context = context; - this.folder = folder; - this.keygen = keygen; - if (!new File(Utilities.path(folder, "conparison-zip-marker.bin")).exists()) { - String f = Utilities.path(folder, "comparison.zip"); - download("https://www.fhir.org/archive/comparison.zip", f); - unzip(f, folder); - } - } - - /** - * Size of the buffer to read/write data - */ - private static final int BUFFER_SIZE = 4096; - /** - * Extracts a zip file specified by the zipFilePath to a directory specified by - * destDirectory (will be created if does not exists) - * @param zipFilePath - * @param destDirectory - * @throws IOException - */ - public void unzip(String zipFilePath, String destDirectory) throws IOException { - File destDir = new File(destDirectory); - if (!destDir.exists()) { - destDir.mkdir(); - } - ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath)); - ZipEntry entry = zipIn.getNextEntry(); - // iterates over entries in the zip file - while (entry != null) { - String filePath = destDirectory + File.separator + entry.getName(); - if (!entry.isDirectory()) { - // if the entry is a file, extracts it - extractFile(zipIn, filePath); - } else { - // if the entry is a directory, make the directory - File dir = new File(filePath); - dir.mkdir(); - } - zipIn.closeEntry(); - entry = zipIn.getNextEntry(); - } - zipIn.close(); - } - /** - * Extracts a zip entry (file entry) - * @param zipIn - * @param filePath - * @throws IOException - */ - private void extractFile(ZipInputStream zipIn, String filePath) throws IOException { - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)); - byte[] bytesIn = new byte[BUFFER_SIZE]; - int read = 0; - while ((read = zipIn.read(bytesIn)) != -1) { - bos.write(bytesIn, 0, read); - } - bos.close(); - } - - public void saveToFile() throws IOException { - String s = new XhtmlComposer(true, true).compose(html); - TextFile.stringToFile(s, Utilities.path(folder, "index.html")); - } - - /** - * Compares 2 capability statements to see if self is a subset of other - * - * e.g. is a system implementing "+otherName+" also an implementation of self? - * - * the output is a series of messages identifying ways in which it is not, - * or warning messages where the algorithm is unable to determine the - * relationship - * - * Note that there are aspects of this question that are not computable. - * the - * - * @param self - * @param other - * @return - * @throws IOException - * @throws FHIRFormatError - * @throws DefinitionException - */ - public CapabilityStatementComparisonOutput isCompatible(String selfName, String otherName, CapabilityStatement self, CapabilityStatement other) throws DefinitionException, FHIRFormatError, IOException { - this.selfName = selfName; - this.otherName = otherName; - this.output = new CapabilityStatementComparisonOutput(); - XhtmlNode x = startHtml(); - - information(x, IssueType.INVARIANT, self.getUrl(), "Comparing "+selfName+" to "+otherName+", to see if a server that implements "+otherName+" also implements "+selfName+""); - information(x, IssueType.INVARIANT, self.getUrl(), " "+selfName+": "+self.getUrl()+"|"+self.getVersion()); - information(x, IssueType.INVARIANT, self.getUrl(), " "+otherName+": "+other.getUrl()+"|"+other.getVersion()); - - if (self.getRest().size() != 1 || self.getRestFirstRep().getMode() != RestfulCapabilityMode.SERVER) - fatal(x, IssueType.INVARIANT, self.getUrl()+"#rest", "The CapabilityStatement Comparison tool can only compare CapabilityStatements with a single server component"); - if (other.getRest().size() != 1 || other.getRestFirstRep().getMode() != RestfulCapabilityMode.SERVER) - fatal(x, IssueType.INVARIANT, other.getUrl()+"#rest", "The CapabilityStatement Comparison tool can only compare CapabilityStatements with a single server component"); - - if (self.getRest().size() == 1 && other.getRest().size() == 1) { - XhtmlNode tbl = startTable(x, self, other); - compareRest(tbl, self.getUrl(), self.getRest().get(0), other.getRest().get(0), output.subset.addRest().setMode(RestfulCapabilityMode.SERVER), output.superset.addRest().setMode(RestfulCapabilityMode.SERVER)); - } - if (folder != null) - saveToFile(); - output.outcome = OperationOutcomeUtilities.createOutcome(output.messages); - sortCapabilityStatement(output.subset); - sortCapabilityStatement(output.superset); - return output; - } - - private void compareRest(XhtmlNode tbl, String path, CapabilityStatementRestComponent self, CapabilityStatementRestComponent other, CapabilityStatementRestComponent intersection, CapabilityStatementRestComponent union) throws DefinitionException, FHIRFormatError, IOException { - compareSecurity(tbl, path, self, other, intersection, union); - - // check resources - List ol = new ArrayList<>(); - List olr = new ArrayList<>(); - ol.addAll(other.getResource()); - for (CapabilityStatementRestResourceComponent r : self.getResource()) { - CapabilityStatementRestResourceComponent o = null; - for (CapabilityStatementRestResourceComponent t : ol) { - if (t.getType().equals(r.getType())) { - o = t; - break; - } - } - XhtmlNode tr = tbl.tr(); - tr.style("background-color: #dddddd"); - tr.td().b().addText(r.getType()); - tr.td().tx("Present"); - - if (o == null) { - union.addResource(r); - XhtmlNode p = tr.td().para("Absent"); - XhtmlNode td = tr.td(); - String s = getConfStatus(r); - if (Utilities.existsInList(s, "SHALL", "SHOULD")) { - error(td, IssueType.NOTFOUND, path+".resource.where(type = '"+r.getType()+"')", selfName+" specifies the resource "+r.getType()+" as "+s+" but "+otherName+" does not cover it"); - p.style("background-color: #ffe6e6; border: 1px solid #ff1a1a; margin-width: 10px"); - } - - tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Conformance"); - genConf(tr.td().para(), r, null); - tr.td().style("background-color: #eeeeee"); - tr.td(); - - tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Profile"); - genProfile(tr.td().para(), r, null); - tr.td().style("background-color: #eeeeee"); - tr.td(); - - tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Interactions"); - genInt(tr.td(), r, null, false); - tr.td().style("background-color: #eeeeee"); - tr.td(); - - tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Search Parameters"); - genSP(tr.td(), r, null, false); - tr.td().style("background-color: #eeeeee"); - tr.td(); - - } else { - olr.add(o); - tr.td().tx("Present"); - tr.td().nbsp(); - compareResource(path+".resource.where(type = '"+r.getType()+"')", r, o, tbl, intersection.addResource().setType(r.getType()), union.addResource().setType(r.getType())); - } - } - for (CapabilityStatementRestResourceComponent t : ol) { - XhtmlNode tr = tbl.tr(); - if (!olr.contains(t)) { - union.addResource(t); - tr.td().addText(t.getType()); - XhtmlNode td = tr.td(); - td.style("background-color: #eeeeee").para("Absent"); - td = tr.td(); - genConf(td, t, null); - genProfile(td, t, null); - genInt(td, t, null, false); - genSP(td, t, null, false); - if (isProhibited(t)) { - error(td, IssueType.INVARIANT, path+".resource", selfName+" does not specify the resource "+t.getType()+" but "+otherName+" prohibits it"); - } - } - } - // check interactions - // check search parameters - // check operation - // check compartments - } - - private void genConf(XhtmlNode x, CapabilityStatementRestResourceComponent r, CapabilityStatementRestResourceComponent other) { - String s = getConfStatus(r); - String so = other != null ? getConfStatus(other) : null; - x.add(same(s == null ? "(not specified)" : s, (s == null && so == null) || (s != null && s.equals(so)))); - } - - private void genProfile(XhtmlNode x, CapabilityStatementRestResourceComponent r, CapabilityStatementRestResourceComponent other) { - String s = getProfile(r); - if (s == null) - s = "(not specified)"; - String so = other == null ? null : getProfile(other) == null ? getProfile(other) : "(not specified)"; - x.add(same(s, (s == null && so == null) || (s != null && s.equals(so)))); - } - - private String getProfile(CapabilityStatementRestResourceComponent r) { - if (r.hasSupportedProfile() && r.getSupportedProfile().size() == 1) - return r.getSupportedProfile().get(0).asStringValue(); - return r.getProfile(); - } - - private void genInt(XhtmlNode td, CapabilityStatementRestResourceComponent r, CapabilityStatementRestResourceComponent other, boolean errorIfNoMatch) { - boolean first = true; - for (ResourceInteractionComponent i : r.getInteraction()) { - if (first) first = false; else td.tx(", "); - if (exists(other, i)) { - td.code().span("background-color: #bbff99; border: 1px solid #44cc00; margin-width: 10px", null).tx(i.getCode().toCode()); - } else if (errorIfNoMatch){ - td.code().span("background-color: #ffe6e6; border: 1px solid #ff1a1a; margin-width: 10px", null).tx(i.getCode().toCode()); - } else { - td.code(i.getCode().toCode()); - } - } - } - - private boolean exists(CapabilityStatementRestResourceComponent other, ResourceInteractionComponent i) { - if (other == null) - return false; - for (ResourceInteractionComponent t : other.getInteraction()) - if (t.getCode().equals(i.getCode())) - return true; - return false; - } - - private void genSP(XhtmlNode td, CapabilityStatementRestResourceComponent r, CapabilityStatementRestResourceComponent other, boolean errorIfNoMatch) { - boolean first = true; - for (CapabilityStatementRestResourceSearchParamComponent i : r.getSearchParam()) { - if (first) first = false; else td.tx(", "); - if (exists(other, i)) { - td.code().span("background-color: #bbff99; border: 1px solid #44cc00; margin-width: 10px", null).tx(i.getName()); - } else if (errorIfNoMatch){ - td.code().span("background-color: #ffe6e6; border: 1px solid #ff1a1a; margin-width: 10px", null).tx(i.getName()); - } else { - td.code(i.getName()); - } - } - } - - private boolean exists(CapabilityStatementRestResourceComponent other, CapabilityStatementRestResourceSearchParamComponent i) { - if (other == null) - return false; - for (CapabilityStatementRestResourceSearchParamComponent t : other.getSearchParam()) - if (t.getName().equals(i.getName())) - return true; - return false; - } - - public void compareSecurity(XhtmlNode tbl, String path, CapabilityStatementRestComponent self, CapabilityStatementRestComponent other, CapabilityStatementRestComponent intersection, CapabilityStatementRestComponent union) { - XhtmlNode tr = tbl.tr(); - tr.td().b().addText("Security"); - tr.td().para(gen(self.getSecurity())); - tr.td().para(gen(other.getSecurity())); - XhtmlNode td = tr.td(); - - if (self.hasSecurity() && !other.hasSecurity()) - error(td, IssueType.CONFLICT, path+".security", selfName+" specifies some security requirements ("+gen(self.getSecurity())+") but "+otherName+" doesn't"); - else if (!self.hasSecurity() && other.hasSecurity()) - error(td, IssueType.CONFLICT, path+".security", selfName+" does not specify security requirements but "+otherName+" does ("+gen(self.getSecurity())+")"); - else if (self.hasSecurity() && other.hasSecurity()) - compareSecurity(td, path+".security", self.getSecurity(), other.getSecurity(), intersection.getSecurity(), union.getSecurity()); - } - - private void compareResource(String path, CapabilityStatementRestResourceComponent self, CapabilityStatementRestResourceComponent other, XhtmlNode tbl, - CapabilityStatementRestResourceComponent intersection, CapabilityStatementRestResourceComponent union) throws DefinitionException, FHIRFormatError, IOException { - XhtmlNode tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Conformance"); - genConf(tr.td(), self, other); - genConf(tr.td(), other, self); - tr.td().nbsp(); - - tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Profile"); - genProfile(tr.td(), self, other); - genProfile(tr.td(), other, self); - compareProfiles(tr.td(), path, getProfile(self), getProfile(other), self.getType(), intersection, union); - - // compare the interactions - compareResourceInteractions(path, self, other, tbl, intersection, union); - compareResourceSearchParams(path, self, other, tbl, intersection, union); - // compare the search parameters - // compare the operations - - // compare the profile? - - } - - private void compareProfiles(XhtmlNode td, String path, String urlL, String urlR, String type, - CapabilityStatementRestResourceComponent intersection, CapabilityStatementRestResourceComponent union) throws DefinitionException, FHIRFormatError, IOException { - if (urlL == null) { - urlL = "http://hl7.org/fhir/StructureDefinition/"+type; - } - if (urlR == null) { - urlR = "http://hl7.org/fhir/StructureDefinition/"+type; - } - StructureDefinition sdL = context.fetchResource(StructureDefinition.class, urlL); - StructureDefinition sdR = context.fetchResource(StructureDefinition.class, urlR); - if (sdL == null) - error(td, IssueType.NOTFOUND, path, "Unable to resolve "+urlL); - if (sdR == null) - error(td, IssueType.NOTFOUND, path, "Unable to resolve "+urlR); - - if (sdL != null && sdR != null && sdL != sdR) { - // ok they are different... - if (sdR.getUrl().equals(sdL.getBaseDefinition())) { - information(td, null, path, "The profile specified by "+selfName+" is inherited from the profile specified by "+otherName); - intersection.setProfile(sdL.getUrl()); - union.setProfile(sdR.getUrl()); - } else if (sdL.getUrl().equals(sdR.getBaseDefinition())) { - information(td, null, path, "The profile specified by "+otherName+" is inherited from the profile specified by "+selfName); - intersection.setProfile(sdR.getUrl()); - union.setProfile(sdL.getUrl()); - } else if (folder != null) { - try { - ProfileComparer pc = new ProfileComparer(context, keygen, folder); - pc.setId("api-ep."+type); - pc.setTitle("Comparison - "+selfName+" vs "+otherName); - pc.setLeftName(selfName+": "+sdL.present()); - pc.setLeftLink(sdL.getUserString("path")); - pc.setRightName(otherName+": "+sdR.present()); - pc.setRightLink(sdR.getUserString("path")); - pc.compareProfiles(sdL, sdR); - System.out.println("Generate Comparison between "+pc.getLeftName()+" and "+pc.getRightName()); - pc.generate(); - - for (ProfileComparison cmp : pc.getComparisons()) { - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, cmp.getSubset().fhirType()+"-"+cmp.getSubset().getId()+".xml")), cmp.getSubset()); - new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, cmp.getSubset().fhirType()+"-"+cmp.getSubset().getId()+".json")), cmp.getSubset()); - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, cmp.getSuperset().fhirType()+"-"+cmp.getSuperset().getId()+".xml")), cmp.getSuperset()); - new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, cmp.getSuperset().fhirType()+"-"+cmp.getSuperset().getId()+".json")), cmp.getSuperset()); - } - for (ValueSet vs : pc.getValuesets()) { - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, vs.fhirType()+"-"+vs.getId()+".xml")), vs); - new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, vs.fhirType()+"-"+vs.getId()+".json")), vs); - } - - intersection.setProfile(pc.getComparisons().get(0).getSubset().getUrl()); - union.setProfile(pc.getComparisons().get(0).getSuperset().getUrl()); - td.ah(pc.getId()+".html").tx("Comparison..."); - td.tx(pc.getErrCount()+" "+Utilities.pluralize("error", pc.getErrCount())); - } catch (Exception e) { - e.printStackTrace(); - error(td, IssueType.EXCEPTION, path, "Error comparing profiles: "+e.getMessage()); - } - } else { - information(td, null, path, "Use the validator to compare the profiles"); - } - } - } - - private void compareResourceInteractions(String path, CapabilityStatementRestResourceComponent self, CapabilityStatementRestResourceComponent other, XhtmlNode tbl, - CapabilityStatementRestResourceComponent intersection, CapabilityStatementRestResourceComponent union) { - XhtmlNode tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Interactions"); - genInt(tr.td(), self, other, true); - genInt(tr.td(), other, self, false); - XhtmlNode td = tr.td(); - List ol = new ArrayList<>(); - List olr = new ArrayList<>(); - ol.addAll(other.getInteraction()); - for (ResourceInteractionComponent r : self.getInteraction()) { - ResourceInteractionComponent o = null; - for (ResourceInteractionComponent t : ol) { - if (t.getCode().equals(r.getCode())) { - o = t; - break; - } - } - union.addInteraction(r); - if (o == null) { - error(td, IssueType.NOTFOUND, path+".interaction.where(code = '"+r.getCode()+"')", selfName+" specifies the interaction "+r.getCode()+" but "+otherName+" does not"); - } else { - intersection.addInteraction(r); - olr.add(o); - } - } - for (ResourceInteractionComponent t : ol) { - union.addInteraction(t); - if (!olr.contains(t) && isProhibited(t)) - error(td, IssueType.CONFLICT, path+".interaction", selfName+" does not specify the interaction "+t.getCode()+" but "+otherName+" prohibits it"); - } - } - - private void compareResourceSearchParams(String path, CapabilityStatementRestResourceComponent self, CapabilityStatementRestResourceComponent other, XhtmlNode tbl, - CapabilityStatementRestResourceComponent intersection, CapabilityStatementRestResourceComponent union) { - XhtmlNode tr = tbl.tr(); - tr.td().para().tx(XhtmlNode.NBSP+" - Search Params"); - genSP(tr.td(), self, other, true); - genSP(tr.td(), other, self, false); - XhtmlNode td = tr.td(); - - List ol = new ArrayList<>(); - List olr = new ArrayList<>(); - ol.addAll(other.getSearchParam()); - for (CapabilityStatementRestResourceSearchParamComponent r : self.getSearchParam()) { - CapabilityStatementRestResourceSearchParamComponent o = null; - for (CapabilityStatementRestResourceSearchParamComponent t : ol) { - if (t.getName().equals(r.getName())) { - o = t; - break; - } - } - union.addSearchParam(r); - if (o == null) { - error(td, IssueType.NOTFOUND, path+".searchParam.where(name = '"+r.getName()+"')", selfName+" specifies the search parameter "+r.getName()+" but "+otherName+" does not"); - } else { - intersection.addSearchParam(r); - olr.add(o); - } - } - for (CapabilityStatementRestResourceSearchParamComponent t : ol) { - if (!olr.contains(t)) - union.addSearchParam(t); - if (!olr.contains(t) && isProhibited(t)) - error(td, IssueType.CONFLICT, path+"", selfName+" does not specify the search parameter "+t.getName()+" but "+otherName+" prohibits it"); - } - - } - - private String getConfStatus(Element t) { - return t.hasExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation") ? - t.getExtensionString("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation") : null; - } - - private boolean isShouldOrShall(Element t) { - return t.hasExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation") && - ("SHALL".equals(t.getExtensionString("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation")) || "SHOULD".equals(t.getExtensionString("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation"))); - } - - private boolean isProhibited(Element t) { - return t.hasExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation") && "SHALL NOT".equals(t.getExtensionString("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation")); - } - - private void compareSecurity(XhtmlNode td, String path, CapabilityStatementRestSecurityComponent self, CapabilityStatementRestSecurityComponent other, - CapabilityStatementRestSecurityComponent intersection, CapabilityStatementRestSecurityComponent union) { - if (self.getCors() && !other.getCors()) - error(td, IssueType.CONFLICT, path+".security.cors", selfName+" specifies CORS but "+otherName+" doesn't"); - else if (!self.getCors() && other.getCors()) - error(td, IssueType.CONFLICT, path+".security.cors", selfName+" does not specify CORS but "+otherName+" does"); - if (self.getCors() || other.getCors()) - union.setCors(true); - if (self.getCors() && other.getCors()) - union.setCors(true); - - List ol = new ArrayList<>(); - List olr = new ArrayList<>(); - ol.addAll(other.getService()); - for (CodeableConcept cc : self.getService()) { - CodeableConcept o = null; - for (CodeableConcept t : ol) { - if (isMatch(t, cc)) { - o = t; - break; - } - } - union.getService().add(cc); - if (o == null) { - error(td, IssueType.CONFLICT, path+".security.cors", selfName+" specifies the security option "+gen(cc)+" but "+otherName+" does not"); - } else { - intersection.getService().add(cc); - olr.add(o); - } - } - for (CodeableConcept cc : ol) { - if (!olr.contains(cc)) { - union.getService().add(cc); - error(td, IssueType.CONFLICT, path+".security.cors", selfName+" does not specify the security option "+gen(cc)+" but "+otherName+" does"); - } - } - } - - private boolean isMatch(CodeableConcept self, CodeableConcept other) { - for (Coding s : self.getCoding()) - for (Coding o : other.getCoding()) - if (isMatch(s, o)) - return true; - return false; - } - - private boolean isMatch(Coding s, Coding o) { - return s.hasCode() && s.getCode().equals(o.getCode()) && s.hasSystem() && s.getSystem().equals(o.getSystem()); - } - - private String gen(CapabilityStatementRestSecurityComponent security) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (CodeableConcept cc : security.getService()) - b.append(gen(cc)); - if (security.getCors()) - b.append("(CORS)"); - if (Utilities.noString(b.toString())) - return "(none specified)"; - return b.toString(); - } - - private String gen(CodeableConcept cc) { - if (cc.hasText()) - return cc.getText(); - if (cc.hasCoding()) - return gen(cc.getCoding().get(0)); - return "?gen-cc?"; - } - - private String gen(Coding coding) { - if (coding.hasDisplay()) - return coding.getDisplay(); - if (coding.hasCode()) - return coding.getCode(); - return "?gen-c?"; - } - - - private XhtmlNode startHtml() { - html = new XhtmlDocument(); - XhtmlNode doc = html.addTag("html"); - XhtmlNode head = doc.addTag("head"); - head.addTag("title").addText("Comparison of "+selfName+" to "+otherName); - head.addTag("link").setAttribute("rel", "stylesheet").setAttribute("href", "fhir.css"); - XhtmlNode body = doc.addTag("body").style("background-color: white"); - - body.h1().addText("Comparison of "+selfName+" to "+otherName); - return body; - } - - private void addMarkdown(XhtmlNode x, String text) throws FHIRFormatError, IOException, DefinitionException { - if (text != null) { - // 1. custom FHIR extensions - while (text.contains("[[[")) { - String left = text.substring(0, text.indexOf("[[[")); - String link = text.substring(text.indexOf("[[[")+3, text.indexOf("]]]")); - String right = text.substring(text.indexOf("]]]")+3); - String url = link; - String[] parts = link.split("\\#"); - StructureDefinition p = context.fetchResource(StructureDefinition.class, parts[0]); - if (p == null) - p = context.fetchTypeDefinition(parts[0]); - if (p == null) - p = context.fetchResource(StructureDefinition.class, link); - if (p != null) { - url = p.getUserString("path"); - if (url == null) - url = p.getUserString("filename"); - } else - throw new DefinitionException("Unable to resolve markdown link "+link); - - text = left+"["+link+"]("+url+")"+right; - } - - // 2. markdown - String s = markdown.process(Utilities.escapeXml(text), "narrative generator"); - XhtmlParser p = new XhtmlParser(); - XhtmlNode m; - try { - m = p.parse("
"+s+"
", "div"); - } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { - throw new FHIRFormatError(e.getMessage(), e); - } - x.getChildNodes().addAll(m.getChildNodes()); - } - } - - - private XhtmlNode startTable(XhtmlNode x, CapabilityStatement self, CapabilityStatement other) { - XhtmlNode tbl = x.table("grid"); - XhtmlNode tr = tbl.tr(); - tr.td().b().nbsp(); - tr.td().b().addText(selfName); - tr.td().b().addText(otherName); - tr.td().b().addText("Comparison"); - return tbl; - } - - private void download(String address, String filename) throws IOException { - URL url = new URL(address); - URLConnection c = url.openConnection(); - InputStream s = c.getInputStream(); - FileOutputStream f = new FileOutputStream(filename); - transfer(s, f, 1024); - f.close(); - } - - - public static void transfer(InputStream in, OutputStream out, int buffer) throws IOException { - byte[] read = new byte[buffer]; // Your buffer size. - while (0 < (buffer = in.read(read))) - out.write(read, 0, buffer); - } - - private void fatal(XhtmlNode x, IssueType type, String path, String message) { - output.messages.add(new ValidationMessage(Source.ProfileComparer, type, path, message, IssueSeverity.FATAL)); - XhtmlNode ul; - if ("ul".equals(x.getName())) { - ul = x; - } else { - ul = null; - for (XhtmlNode c : x.getChildNodes()) { - if ("ul".equals(c.getName())) { - ul = c; - } - } - if (ul == null) { - ul = x.ul(); - } - } - ul.li().b().style("color: maroon").addText(message); - } - - private void error(XhtmlNode x, IssueType type, String path, String message) { - output.messages.add(new ValidationMessage(Source.ProfileComparer, type, path, message, IssueSeverity.ERROR)); - XhtmlNode ul; - if ("ul".equals(x.getName())) { - ul = x; - } else { - ul = null; - for (XhtmlNode c : x.getChildNodes()) { - if ("ul".equals(c.getName())) { - ul = c; - } - } - if (ul == null) { - ul = x.ul(); - } - } - ul.li().b().style("color: maroon").addText(message); - } - - private void information(XhtmlNode x, IssueType type, String path, String message) { - if (type != null) - output.messages.add(new ValidationMessage(Source.ProfileComparer, type, path, message, IssueSeverity.INFORMATION)); - XhtmlNode ul; - if ("ul".equals(x.getName())) { - ul = x; - } else { - ul = null; - for (XhtmlNode c : x.getChildNodes()) { - if ("ul".equals(c.getName())) { - ul = c; - } - } - if (ul == null) { - ul = x.ul(); - } - } - ul.li().addText(message); - } - - private XhtmlNode same(String text, boolean test) { - XhtmlNode span = new XhtmlNode(NodeType.Element, "span"); - if (test) - span.style("background-color: #bbff99; border: 1px solid #44cc00; margin-width: 10px"); - span.tx(text); - return span; - } - - private int compareStr(String l, String r) { - if (l == null) { - if (r == null) - return (r == null) ? 0 : -1; - } - if (r == null) - return 1; - return l.compareTo(r); - } - - public class CapabilityStatementRestSorter implements Comparator { - @Override - public int compare(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r) { - return compareStr(l.hasMode() ? l.getMode().toCode() : null, r.hasMode() ? r.getMode().toCode() : null); - } - } - - public class CapabilityStatementRestResourceSorter implements Comparator { - @Override - public int compare(CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r) { - return compareStr(l.getType(), r.getType()); - } - } - - public class CapabilityStatementRestResourceInteractionSorter implements Comparator { - @Override - public int compare(ResourceInteractionComponent l, ResourceInteractionComponent r) { - return compareStr(l.hasCode() ? l.getCode().toCode() : null, r.hasCode() ? r.getCode().toCode() : null); - } - } - - public class SystemInteractionComponentSorter implements Comparator { - @Override - public int compare(SystemInteractionComponent l, SystemInteractionComponent r) { - return compareStr(l.hasCode() ? l.getCode().toCode() : null, r.hasCode() ? r.getCode().toCode() : null); - } - } - - public class SearchParamSorter implements Comparator { - @Override - public int compare(CapabilityStatementRestResourceSearchParamComponent l, CapabilityStatementRestResourceSearchParamComponent r) { - return compareStr(l.getName(), r.getName()); - } - } - - public class OperationSorter implements Comparator { - @Override - public int compare(CapabilityStatementRestResourceOperationComponent l, CapabilityStatementRestResourceOperationComponent r) { - return compareStr(l.getName(), r.getName()); - } - } - - public class CanonicalTypeSorter implements Comparator { - @Override - public int compare(CanonicalType l, CanonicalType r) { - return compareStr(l.getValue(), r.getValue()); - } - } - - public class StringTypeSorter implements Comparator { - @Override - public int compare(StringType l, StringType r) { - return compareStr(l.getValue(), r.getValue()); - } - } - - public class EnumSorter implements Comparator { - @Override - public int compare(Enumeration l, Enumeration r) { - return compareStr(l.getCode(), r.getCode()); - } - } - - public class ExtensionSorter implements Comparator { - @Override - public int compare(Extension l, Extension r) { - return compareStr(l.getUrl(), r.getUrl()); - } - } - - public class CodingSorter implements Comparator { - @Override - public int compare(Coding l, Coding r) { - return compareStr(""+l.getSystem()+"#"+l.getCode(), ""+r.getSystem()+"#"+r.getCode()); - } - } - public class CodeableConceptSorter implements Comparator { - @Override - public int compare(CodeableConcept l, CodeableConcept r) { - Collections.sort(l.getCoding(), new CodingSorter()); - Collections.sort(r.getCoding(), new CodingSorter()); - return compareStr(cgen(l), cgen(r)); - } - - private String cgen(CodeableConcept cc) { - if (cc.hasCoding()) - return ""+cc.getCodingFirstRep().getSystem()+"#"+cc.getCodingFirstRep().getCode(); - return "~"+cc.getText(); - } - } - - private void sortCapabilityStatement(CapabilityStatement cs) { - sortExtensions(cs); - Collections.sort(cs.getRest(), new CapabilityStatementRestSorter()); - for (CapabilityStatementRestComponent csr : cs.getRest()) { - Collections.sort(csr.getSecurity().getService(), new CodeableConceptSorter()); - Collections.sort(csr.getCompartment(), new CanonicalTypeSorter()); - Collections.sort(csr.getResource(), new CapabilityStatementRestResourceSorter()); - for (CapabilityStatementRestResourceComponent r : csr.getResource()) { - Collections.sort(r.getSupportedProfile(), new CanonicalTypeSorter()); - Collections.sort(r.getInteraction(), new CapabilityStatementRestResourceInteractionSorter()); - Collections.sort(r.getReferencePolicy(), new EnumSorter()); - Collections.sort(r.getSearchInclude(), new StringTypeSorter()); - Collections.sort(r.getSearchRevInclude(), new StringTypeSorter()); - Collections.sort(r.getSearchParam(), new SearchParamSorter()); - Collections.sort(r.getOperation(), new OperationSorter()); - } - Collections.sort(csr.getInteraction(), new SystemInteractionComponentSorter()); - Collections.sort(csr.getSearchParam(), new SearchParamSorter()); - Collections.sort(csr.getOperation(), new OperationSorter()); - } - } - - private void sortExtensions(DomainResource dr) { - Collections.sort(dr.getExtension(), new ExtensionSorter()); - for (Property p : dr.children()) { - for (Base b : p.getValues()) { - if (b instanceof Element) - sortExtensions((Element) b); - } - } - } - - private void sortExtensions(Element e) { - Collections.sort(e.getExtension(), new ExtensionSorter()); - for (Property p : e.children()) { - for (Base b : p.getValues()) { - if (b instanceof Element) - sortExtensions((Element) b); - } - } - } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileComparer.java deleted file mode 100644 index b2e099182..000000000 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileComparer.java +++ /dev/null @@ -1,1638 +0,0 @@ -package org.hl7.fhir.r5.conformance; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ - - - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.hl7.fhir.exceptions.DefinitionException; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.formats.IParser; -import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.Coding; -import org.hl7.fhir.r5.model.DataType; -import org.hl7.fhir.r5.model.ElementDefinition; -import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; -import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; -import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; -import org.hl7.fhir.r5.model.Enumerations.BindingStrength; -import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.IntegerType; -import org.hl7.fhir.r5.model.PrimitiveType; -import org.hl7.fhir.r5.model.StringType; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; -import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; -import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r5.renderers.RendererFactory; -import org.hl7.fhir.r5.renderers.utils.RenderingContext; -import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; -import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; -import org.hl7.fhir.r5.utils.DefinitionNavigator; -import org.hl7.fhir.r5.utils.EOperationOutcome; -import org.hl7.fhir.r5.utils.KeyGenerator; -import org.hl7.fhir.r5.utils.ToolingExtensions; -import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.TextFile; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.validation.ValidationMessage; -import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; -import org.hl7.fhir.utilities.validation.ValidationMessage.Source; -import org.hl7.fhir.utilities.xhtml.XhtmlComposer; - -/** - * A engine that generates difference analysis between two sets of structure - * definitions, typically from 2 different implementation guides. - * - * How this class works is that you create it with access to a bunch of underying - * resources that includes all the structure definitions from both implementation - * guides - * - * Once the class is created, you repeatedly pass pairs of structure definitions, - * one from each IG, building up a web of difference analyses. This class will - * automatically process any internal comparisons that it encounters - * - * When all the comparisons have been performed, you can then generate a variety - * of output formats - * - * @author Grahame Grieve - * - */ -public class ProfileComparer implements ProfileKnowledgeProvider { - - private IWorkerContext context; - private KeyGenerator keygen; - private String folder; - - public ProfileComparer(IWorkerContext context, KeyGenerator keygen, String folder) throws IOException { - super(); - this.context = context; - this.keygen = keygen; - this.folder = folder; - for (Entry e : context.getBinaries().entrySet()) { - TextFile.bytesToFile(e.getValue(), Utilities.path(folder, e.getKey())); - } - } - - public ProfileComparer(IWorkerContext context, String folder) throws IOException { - super(); - this.context = context; - this.folder = folder; - for (Entry e : context.getBinaries().entrySet()) { - TextFile.bytesToFile(e.getValue(), Utilities.path(folder, e.getKey())); - } - } - - private static final int BOTH_NULL = 0; - private static final int EITHER_NULL = 1; - - public class ProfileComparison { - private String id; - /** - * the first of two structures that were compared to generate this comparison - * - * In a few cases - selection of example content and value sets - left gets - * preference over right - */ - private StructureDefinition left; - - /** - * the second of two structures that were compared to generate this comparison - * - * In a few cases - selection of example content and value sets - left gets - * preference over right - */ - private StructureDefinition right; - - - public String getId() { - return id; - } - private String leftName() { - return left.getName(); - } - private String rightName() { - return right.getName(); - } - - /** - * messages generated during the comparison. There are 4 grades of messages: - * information - a list of differences between structures - * warnings - notifies that the comparer is unable to fully compare the structures (constraints differ, open value sets) - * errors - where the structures are incompatible - * fatal errors - some error that prevented full analysis - * - * @return - */ - private List messages = new ArrayList(); - - /** - * The structure that describes all instances that will conform to both structures - */ - private StructureDefinition subset; - - /** - * The structure that describes all instances that will conform to either structures - */ - private StructureDefinition superset; - - public StructureDefinition getLeft() { - return left; - } - - public StructureDefinition getRight() { - return right; - } - - public List getMessages() { - return messages; - } - - public StructureDefinition getSubset() { - return subset; - } - - public StructureDefinition getSuperset() { - return superset; - } - - private boolean ruleEqual(String path, ElementDefinition ed, String vLeft, String vRight, String description, boolean nullOK) { - if (vLeft == null && vRight == null && nullOK) - return true; - if (vLeft == null && vRight == null) { - messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, description+" and not null (null/null)", ValidationMessage.IssueSeverity.ERROR)); - if (ed != null) - status(ed, ProfileUtilities.STATUS_ERROR); - } - if (vLeft == null || !vLeft.equals(vRight)) { - messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, description+" ("+vLeft+"/"+vRight+")", ValidationMessage.IssueSeverity.ERROR)); - if (ed != null) - status(ed, ProfileUtilities.STATUS_ERROR); - } - return true; - } - - private boolean ruleCompares(ElementDefinition ed, DataType vLeft, DataType vRight, String path, int nullStatus) throws IOException { - if (vLeft == null && vRight == null && nullStatus == BOTH_NULL) - return true; - if (vLeft == null && vRight == null) { - messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Must be the same and not null (null/null)", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - } - if (vLeft == null && nullStatus == EITHER_NULL) - return true; - if (vRight == null && nullStatus == EITHER_NULL) - return true; - if (vLeft == null || vRight == null || !Base.compareDeep(vLeft, vRight, false)) { - messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Must be the same ("+toString(vLeft)+"/"+toString(vRight)+")", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - } - return true; - } - - private boolean rule(ElementDefinition ed, boolean test, String path, String message) { - if (!test) { - messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, message, ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - } - return test; - } - - private boolean ruleEqual(ElementDefinition ed, boolean vLeft, boolean vRight, String path, String elementName) { - if (vLeft != vRight) { - messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, elementName+" must be the same ("+vLeft+"/"+vRight+")", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - } - return true; - } - - private String toString(DataType val) throws IOException { - if (val instanceof PrimitiveType) - return "\"" + ((PrimitiveType) val).getValueAsString()+"\""; - - IParser jp = context.newJsonParser(); - return jp.composeString(val, "value"); - } - - public int getErrorCount() { - int c = 0; - for (ValidationMessage vm : messages) - if (vm.getLevel() == ValidationMessage.IssueSeverity.ERROR) - c++; - return c; - } - - public int getWarningCount() { - int c = 0; - for (ValidationMessage vm : messages) - if (vm.getLevel() == ValidationMessage.IssueSeverity.WARNING) - c++; - return c; - } - - public int getHintCount() { - int c = 0; - for (ValidationMessage vm : messages) - if (vm.getLevel() == ValidationMessage.IssueSeverity.INFORMATION) - c++; - return c; - } - } - - /** - * Value sets used in the subset and superset - */ - private List valuesets = new ArrayList(); - private List comparisons = new ArrayList(); - private String id; - private String title; - private String leftPrefix; - private String rightPrefix; - private String leftLink; - private String leftName; - private String rightLink; - private String rightName; - - - public List getValuesets() { - return valuesets; - } - - public void status(ElementDefinition ed, int value) { - ed.setUserData(ProfileUtilities.UD_ERROR_STATUS, Math.max(value, ed.getUserInt("error-status"))); - } - - public List getComparisons() { - return comparisons; - } - - /** - * Compare left and right structure definitions to see whether they are consistent or not - * - * Note that left and right are arbitrary choices. In one respect, left - * is 'preferred' - the left's example value and data sets will be selected - * over the right ones in the common structure definition - * @throws DefinitionException - * @throws IOException - * @throws FHIRFormatError - * - * @ - */ - public ProfileComparison compareProfiles(StructureDefinition left, StructureDefinition right) throws DefinitionException, IOException, FHIRFormatError { - ProfileComparison outcome = new ProfileComparison(); - outcome.left = left; - outcome.right = right; - - if (left == null) - throw new DefinitionException("No StructureDefinition provided (left)"); - if (right == null) - throw new DefinitionException("No StructureDefinition provided (right)"); - if (!left.hasSnapshot()) - throw new DefinitionException("StructureDefinition has no snapshot (left: "+outcome.leftName()+")"); - if (!right.hasSnapshot()) - throw new DefinitionException("StructureDefinition has no snapshot (right: "+outcome.rightName()+")"); - if (left.getSnapshot().getElement().isEmpty()) - throw new DefinitionException("StructureDefinition snapshot is empty (left: "+outcome.leftName()+")"); - if (right.getSnapshot().getElement().isEmpty()) - throw new DefinitionException("StructureDefinition snapshot is empty (right: "+outcome.rightName()+")"); - - for (ProfileComparison pc : comparisons) - if (pc.left.getUrl().equals(left.getUrl()) && pc.right.getUrl().equals(right.getUrl())) - return pc; - - outcome.id = Integer.toString(comparisons.size()+1); - comparisons.add(outcome); - - DefinitionNavigator ln = new DefinitionNavigator(context, left); - DefinitionNavigator rn = new DefinitionNavigator(context, right); - - // from here on in, any issues go in messages - outcome.superset = new StructureDefinition(); - outcome.subset = new StructureDefinition(); - keygen.genId(outcome.subset); - keygen.genId(outcome.superset); - if (outcome.ruleEqual(ln.path(), null,ln.path(), rn.path(), "Base Type is not compatible", false)) { - if (compareElements(outcome, ln.path(), ln, rn, null)) { - outcome.subset.setName("intersection of "+outcome.leftName()+" and "+outcome.rightName()); - outcome.subset.setStatus(PublicationStatus.DRAFT); - outcome.subset.setKind(outcome.left.getKind()); - outcome.subset.setType(outcome.left.getType()); - outcome.subset.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/"+outcome.subset.getType()); - outcome.subset.setDerivation(TypeDerivationRule.CONSTRAINT); - outcome.subset.setAbstract(false); - outcome.superset.setName("union of "+outcome.leftName()+" and "+outcome.rightName()); - outcome.superset.setStatus(PublicationStatus.DRAFT); - outcome.superset.setKind(outcome.left.getKind()); - outcome.superset.setType(outcome.left.getType()); - outcome.superset.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/"+outcome.subset.getType()); - outcome.superset.setAbstract(false); - outcome.superset.setDerivation(TypeDerivationRule.CONSTRAINT); - } else { - outcome.subset = null; - outcome.superset = null; - } - } - return outcome; - } - - /** - * left and right refer to the same element. Are they compatible? - * @param outcome - * @param outcome - * @param path - * @param left - * @param right - * @- if there's a problem that needs fixing in this code - * @throws DefinitionException - * @throws IOException - * @throws FHIRFormatError - */ - private boolean compareElements(ProfileComparison outcome, String path, DefinitionNavigator left, DefinitionNavigator right, String sliceName) throws DefinitionException, IOException, FHIRFormatError { -// preconditions: - assert(path != null); - assert(left != null); - assert(right != null); - assert(left.path().equals(right.path())); - - // we ignore slicing right now - we're going to clone the root one anyway, and then think about clones - // simple stuff - ElementDefinition subset = new ElementDefinition(); - subset.setPath(left.path()); - if (sliceName != null) - subset.setSliceName(sliceName); - - // not allowed to be different: - subset.getRepresentation().addAll(left.current().getRepresentation()); // can't be bothered even testing this one - if (!outcome.ruleCompares(subset, left.current().getDefaultValue(), right.current().getDefaultValue(), path+".defaultValue[x]", BOTH_NULL)) - return false; - subset.setDefaultValue(left.current().getDefaultValue()); - if (!outcome.ruleEqual(path, subset, left.current().getMeaningWhenMissing(), right.current().getMeaningWhenMissing(), "meaningWhenMissing Must be the same", true)) - return false; - subset.setMeaningWhenMissing(left.current().getMeaningWhenMissing()); - if (!outcome.ruleEqual(subset, left.current().getIsModifier(), right.current().getIsModifier(), path, "isModifier")) - return false; - subset.setIsModifier(left.current().getIsModifier()); - if (!outcome.ruleEqual(subset, left.current().getIsSummary(), right.current().getIsSummary(), path, "isSummary")) - return false; - subset.setIsSummary(left.current().getIsSummary()); - - // descriptive properties from ElementDefinition - merge them: - subset.setLabel(mergeText(subset, outcome, path, "label", left.current().getLabel(), right.current().getLabel())); - subset.setShort(mergeText(subset, outcome, path, "short", left.current().getShort(), right.current().getShort())); - subset.setDefinition(mergeText(subset, outcome, path, "definition", left.current().getDefinition(), right.current().getDefinition())); - subset.setComment(mergeText(subset, outcome, path, "comments", left.current().getComment(), right.current().getComment())); - subset.setRequirements(mergeText(subset, outcome, path, "requirements", left.current().getRequirements(), right.current().getRequirements())); - subset.getCode().addAll(mergeCodings(left.current().getCode(), right.current().getCode())); - subset.getAlias().addAll(mergeStrings(left.current().getAlias(), right.current().getAlias())); - subset.getMapping().addAll(mergeMappings(left.current().getMapping(), right.current().getMapping())); - // left will win for example - subset.setExample(left.current().hasExample() ? left.current().getExample() : right.current().getExample()); - - subset.setMustSupport(left.current().getMustSupport() || right.current().getMustSupport()); - ElementDefinition superset = subset.copy(); - - - // compare and intersect - superset.setMin(unionMin(left.current().getMin(), right.current().getMin())); - superset.setMax(unionMax(left.current().getMax(), right.current().getMax())); - subset.setMin(intersectMin(left.current().getMin(), right.current().getMin())); - subset.setMax(intersectMax(left.current().getMax(), right.current().getMax())); - outcome.rule(subset, subset.getMax().equals("*") || Integer.parseInt(subset.getMax()) >= subset.getMin(), path, "Cardinality Mismatch: "+card(left)+"/"+card(right)); - - superset.getType().addAll(unionTypes(path, left.current().getType(), right.current().getType())); - subset.getType().addAll(intersectTypes(subset, outcome, path, left.current().getType(), right.current().getType())); - outcome.rule(subset, !subset.getType().isEmpty() || (!left.current().hasType() && !right.current().hasType()), path, "Type Mismatch:\r\n "+typeCode(left)+"\r\n "+typeCode(right)); -// -// - superset.setMaxLengthElement(unionMaxLength(left.current().getMaxLength(), right.current().getMaxLength())); - subset.setMaxLengthElement(intersectMaxLength(left.current().getMaxLength(), right.current().getMaxLength())); - if (left.current().hasBinding() || right.current().hasBinding()) { - compareBindings(outcome, subset, superset, path, left.current(), right.current()); - } - - // note these are backwards - superset.getConstraint().addAll(intersectConstraints(path, left.current().getConstraint(), right.current().getConstraint())); - subset.getConstraint().addAll(unionConstraints(subset, outcome, path, left.current().getConstraint(), right.current().getConstraint())); - - // add the children - outcome.subset.getSnapshot().getElement().add(subset); - outcome.superset.getSnapshot().getElement().add(superset); - boolean ret = compareChildren(subset, outcome, path, left, right); - - // now process the slices - if (left.current().hasSlicing() || right.current().hasSlicing()) { - assert sliceName == null; - if (isExtension(left.path())) - return compareExtensions(outcome, path, superset, subset, left, right); -// return true; - else { - ElementDefinitionSlicingComponent slicingL = left.current().getSlicing(); - ElementDefinitionSlicingComponent slicingR = right.current().getSlicing(); - // well, this is tricky. If one is sliced, and the other is not, then in general, the union just ignores the slices, and the intersection is the slices. - if (left.current().hasSlicing() && !right.current().hasSlicing()) { - // the super set is done. Any restrictions in the slices are irrelevant to what the super set says, except that we're going sum up the value sets if we can (for documentation purposes) (todo) - // the minimum set is the slicing specified in the slicer - subset.setSlicing(slicingL); - // stick everything from the right to do with the slices to the subset - copySlices(outcome.subset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), left.slices()); - } else if (!left.current().hasSlicing() && right.current().hasSlicing()) { - // the super set is done. Any restrictions in the slices are irrelevant to what the super set says, except that we're going sum up the value sets if we can (for documentation purposes) (todo) - // the minimum set is the slicing specified in the slicer - subset.setSlicing(slicingR); - // stick everything from the right to do with the slices to the subset - copySlices(outcome.subset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), right.slices()); - } else if (isTypeSlicing(slicingL) || isTypeSlicing(slicingR)) { - superset.getSlicing().setRules(SlicingRules.OPEN).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); - subset.getSlicing().setRules(slicingL.getRules() == SlicingRules.CLOSED || slicingR.getRules() == SlicingRules.CLOSED ? SlicingRules.OPEN : SlicingRules.CLOSED).setOrdered(false).addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); - - // the superset is the union of the types - // the subset is the intersection of them - List handled = new ArrayList<>(); - for (DefinitionNavigator t : left.slices()) { - DefinitionNavigator r = findMatchingSlice(right.slices(), t); - if (r == null) { - copySlice(outcome.superset.getSnapshot().getElement(), left.getStructure().getSnapshot().getElement(), t); - } else { - handled.add(r); - ret = compareElements(outcome, path+":"+t.current().getSliceName(), t, r, t.current().getSliceName()) && ret; - } - } - for (DefinitionNavigator t : right.slices()) { - if (!handled.contains(t)) { - copySlice(outcome.superset.getSnapshot().getElement(), right.getStructure().getSnapshot().getElement(), t); - } - } - } else if (slicingMatches(slicingL, slicingR)) { - // if it's the same, we can try matching the slices - though we might have to give up without getting matches correct - // there amy be implied consistency we can't reason about - throw new DefinitionException("Slicing matches but is not handled yet at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+")"); - } else { - // if the slicing is different, we can't compare them - or can we? - throw new DefinitionException("Slicing doesn't match at "+left.current().getId()+": ("+ProfileUtilities.summarizeSlicing(slicingL)+" / "+ProfileUtilities.summarizeSlicing(slicingR)+")"); - } - } - // todo: name - } - return ret; - } - - - private DefinitionNavigator findMatchingSlice(List slices, DefinitionNavigator tgt) { - for (DefinitionNavigator t : slices) { - if (sliceMatchesByType(t, tgt)) - return t; - } - return null; - } - - private boolean sliceMatchesByType(DefinitionNavigator t, DefinitionNavigator tgt) { - return t.current().typeSummary().equals(tgt.current().typeSummary()); - } - - private void copySlices(List target, List source, List list) { - for (DefinitionNavigator slice : list) { - copySlice(target, source, slice); - } - } - - public void copySlice(List target, List source, DefinitionNavigator slice) { - target.add(slice.current().copy()); - int i = source.indexOf(slice.current())+1; - while (i < source.size() && source.get(i).getPath().startsWith(slice.current().getPath()+".")) { - target.add(source.get(i).copy()); - i++; - } - } - - private boolean isTypeSlicing(ElementDefinitionSlicingComponent slicing) { - if (slicing.getDiscriminator().size() == 1 && slicing.getDiscriminatorFirstRep().getType() == DiscriminatorType.TYPE && "$this".equals(slicing.getDiscriminatorFirstRep().getPath())) - return true; - return false; - } - - private boolean slicingMatches(ElementDefinitionSlicingComponent l, ElementDefinitionSlicingComponent r) { - if (l.getDiscriminator().size() != r.getDiscriminator().size()) - return false; - for (int i = 0; i < l.getDiscriminator().size(); i++) { - if (!slicingMatches(l.getDiscriminator().get(i), r.getDiscriminator().get(i))) - return false; - } - return l.getOrdered() == r.getOrdered(); - } - - private boolean slicingMatches(ElementDefinitionSlicingDiscriminatorComponent l, ElementDefinitionSlicingDiscriminatorComponent r) { - return l.getType() == r.getType() && l.getPath().equals(r.getPath()); - } - - private class ExtensionUsage { - private DefinitionNavigator defn; - private int minSuperset; - private int minSubset; - private String maxSuperset; - private String maxSubset; - private boolean both = false; - - public ExtensionUsage(DefinitionNavigator defn, int min, String max) { - super(); - this.defn = defn; - this.minSubset = min; - this.minSuperset = min; - this.maxSubset = max; - this.maxSuperset = max; - } - - } - private boolean compareExtensions(ProfileComparison outcome, String path, ElementDefinition superset, ElementDefinition subset, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException { - // for now, we don't handle sealed (or ordered) extensions - - // for an extension the superset is all extensions, and the subset is.. all extensions - well, unless thay are sealed. - // but it's not useful to report that. instead, we collate the defined ones, and just adjust the cardinalities - Map map = new HashMap(); - - if (left.slices() != null) - for (DefinitionNavigator ex : left.slices()) { - String url = ex.current().getType().get(0).getProfile().get(0).getValue(); - if (map.containsKey(url)) - throw new DefinitionException("Duplicate Extension "+url+" at "+path); - else - map.put(url, new ExtensionUsage(ex, ex.current().getMin(), ex.current().getMax())); - } - if (right.slices() != null) - for (DefinitionNavigator ex : right.slices()) { - String url = ex.current().getType().get(0).getProfile().get(0).getValue(); - if (map.containsKey(url)) { - ExtensionUsage exd = map.get(url); - exd.minSuperset = unionMin(exd.defn.current().getMin(), ex.current().getMin()); - exd.maxSuperset = unionMax(exd.defn.current().getMax(), ex.current().getMax()); - exd.minSubset = intersectMin(exd.defn.current().getMin(), ex.current().getMin()); - exd.maxSubset = intersectMax(exd.defn.current().getMax(), ex.current().getMax()); - exd.both = true; - outcome.rule(subset, exd.maxSubset.equals("*") || Integer.parseInt(exd.maxSubset) >= exd.minSubset, path, "Cardinality Mismatch on extension: "+card(exd.defn)+"/"+card(ex)); - } else { - map.put(url, new ExtensionUsage(ex, ex.current().getMin(), ex.current().getMax())); - } - } - List names = new ArrayList(); - names.addAll(map.keySet()); - Collections.sort(names); - for (String name : names) { - ExtensionUsage exd = map.get(name); - if (exd.both) - outcome.subset.getSnapshot().getElement().add(exd.defn.current().copy().setMin(exd.minSubset).setMax(exd.maxSubset)); - outcome.superset.getSnapshot().getElement().add(exd.defn.current().copy().setMin(exd.minSuperset).setMax(exd.maxSuperset)); - } - return true; - } - - private boolean isExtension(String path) { - return path.endsWith(".extension") || path.endsWith(".modifierExtension"); - } - - private boolean compareChildren(ElementDefinition ed, ProfileComparison outcome, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError { - List lc = left.children(); - List rc = right.children(); - // it's possible that one of these profiles walks into a data type and the other doesn't - // if it does, we have to load the children for that data into the profile that doesn't - // walk into it - if (lc.isEmpty() && !rc.isEmpty() && right.current().getType().size() == 1 && left.hasTypeChildren(right.current().getType().get(0))) - lc = left.childrenFromType(right.current().getType().get(0)); - if (rc.isEmpty() && !lc.isEmpty() && left.current().getType().size() == 1 && right.hasTypeChildren(left.current().getType().get(0))) - rc = right.childrenFromType(left.current().getType().get(0)); - if (lc.size() != rc.size()) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Different number of children at "+path+" ("+Integer.toString(lc.size())+"/"+Integer.toString(rc.size())+")", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - return false; - } else { - for (int i = 0; i < lc.size(); i++) { - DefinitionNavigator l = lc.get(i); - DefinitionNavigator r = rc.get(i); - String cpath = comparePaths(l.path(), r.path(), path, l.nameTail(), r.nameTail()); - if (cpath != null) { - if (!compareElements(outcome, cpath, l, r, null)) - return false; - } else { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Different path at "+path+"["+Integer.toString(i)+"] ("+l.path()+"/"+r.path()+")", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - return false; - } - } - } - return true; - } - - private String comparePaths(String path1, String path2, String path, String tail1, String tail2) { - if (tail1.equals(tail2)) { - return path+"."+tail1; - } else if (tail1.endsWith("[x]") && tail2.startsWith(tail1.substring(0, tail1.length()-3))) { - return path+"."+tail1; - } else if (tail2.endsWith("[x]") && tail1.startsWith(tail2.substring(0, tail2.length()-3))) { - return path+"."+tail2; - } else - return null; - } - - private boolean compareBindings(ProfileComparison outcome, ElementDefinition subset, ElementDefinition superset, String path, ElementDefinition lDef, ElementDefinition rDef) throws FHIRFormatError { - assert(lDef.hasBinding() || rDef.hasBinding()); - if (!lDef.hasBinding()) { - subset.setBinding(rDef.getBinding()); - // technically, the super set is unbound, but that's not very useful - so we use the provided on as an example - superset.setBinding(rDef.getBinding().copy()); - superset.getBinding().setStrength(BindingStrength.EXAMPLE); - return true; - } - if (!rDef.hasBinding()) { - subset.setBinding(lDef.getBinding()); - superset.setBinding(lDef.getBinding().copy()); - superset.getBinding().setStrength(BindingStrength.EXAMPLE); - return true; - } - ElementDefinitionBindingComponent left = lDef.getBinding(); - ElementDefinitionBindingComponent right = rDef.getBinding(); - if (Base.compareDeep(left, right, false)) { - subset.setBinding(left); - superset.setBinding(right); - } - - // if they're both examples/preferred then: - // subset: left wins if they're both the same - // superset: - if (isPreferredOrExample(left) && isPreferredOrExample(right)) { - if (right.getStrength() == BindingStrength.PREFERRED && left.getStrength() == BindingStrength.EXAMPLE && !Base.compareDeep(left.getValueSet(), right.getValueSet(), false)) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Example/preferred bindings differ at "+path+" using binding from "+outcome.rightName(), ValidationMessage.IssueSeverity.INFORMATION)); - status(subset, ProfileUtilities.STATUS_HINT); - subset.setBinding(right); - superset.setBinding(unionBindings(superset, outcome, path, left, right)); - } else { - if ((right.getStrength() != BindingStrength.EXAMPLE || left.getStrength() != BindingStrength.EXAMPLE) && !Base.compareDeep(left.getValueSet(), right.getValueSet(), false) ) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Example/preferred bindings differ at "+path+" using binding from "+outcome.leftName(), ValidationMessage.IssueSeverity.INFORMATION)); - status(subset, ProfileUtilities.STATUS_HINT); - } - subset.setBinding(left); - superset.setBinding(unionBindings(superset, outcome, path, left, right)); - } - return true; - } - // if either of them are extensible/required, then it wins - if (isPreferredOrExample(left)) { - subset.setBinding(right); - superset.setBinding(unionBindings(superset, outcome, path, left, right)); - return true; - } - if (isPreferredOrExample(right)) { - subset.setBinding(left); - superset.setBinding(unionBindings(superset, outcome, path, left, right)); - return true; - } - - // ok, both are extensible or required. - ElementDefinitionBindingComponent subBinding = new ElementDefinitionBindingComponent(); - subset.setBinding(subBinding); - ElementDefinitionBindingComponent superBinding = new ElementDefinitionBindingComponent(); - superset.setBinding(superBinding); - subBinding.setDescription(mergeText(subset, outcome, path, "description", left.getDescription(), right.getDescription())); - superBinding.setDescription(mergeText(subset, outcome, null, "description", left.getDescription(), right.getDescription())); - if (left.getStrength() == BindingStrength.REQUIRED || right.getStrength() == BindingStrength.REQUIRED) - subBinding.setStrength(BindingStrength.REQUIRED); - else - subBinding.setStrength(BindingStrength.EXTENSIBLE); - if (left.getStrength() == BindingStrength.EXTENSIBLE || right.getStrength() == BindingStrength.EXTENSIBLE) - superBinding.setStrength(BindingStrength.EXTENSIBLE); - else - superBinding.setStrength(BindingStrength.REQUIRED); - - if (Base.compareDeep(left.getValueSet(), right.getValueSet(), false)) { - subBinding.setValueSet(left.getValueSet()); - superBinding.setValueSet(left.getValueSet()); - return true; - } else if (!left.hasValueSet()) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "No left Value set at "+path, ValidationMessage.IssueSeverity.ERROR)); - return true; - } else if (!right.hasValueSet()) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "No right Value set at "+path, ValidationMessage.IssueSeverity.ERROR)); - return true; - } else { - // ok, now we compare the value sets. This may be unresolvable. - ValueSet lvs = resolveVS(outcome.left, left.getValueSet()); - ValueSet rvs = resolveVS(outcome.right, right.getValueSet()); - if (lvs == null) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Unable to resolve left value set "+left.getValueSet().toString()+" at "+path, ValidationMessage.IssueSeverity.ERROR)); - return true; - } else if (rvs == null) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Unable to resolve right value set "+right.getValueSet().toString()+" at "+path, ValidationMessage.IssueSeverity.ERROR)); - return true; - } else { - // first, we'll try to do it by definition - ValueSet cvs = intersectByDefinition(lvs, rvs); - if(cvs == null) { - // if that didn't work, we'll do it by expansion - ValueSetExpansionOutcome le; - ValueSetExpansionOutcome re; - try { - le = context.expandVS(lvs, true, false); - re = context.expandVS(rvs, true, false); - if (le.getError() != null) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value set "+lvs.getUrl()+" could not be expanded", ValidationMessage.IssueSeverity.ERROR)); - } else if (re.getError() != null) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value set "+rvs.getUrl()+" could not be expanded", ValidationMessage.IssueSeverity.ERROR)); - } else if (!closed(le.getValueset())) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value set "+lvs.getUrl()+" is not closed, so can't be compased", ValidationMessage.IssueSeverity.ERROR)); - } else if (!closed(re.getValueset())) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value set "+rvs.getUrl()+" is not closed, so can't be compased", ValidationMessage.IssueSeverity.ERROR)); - } else { - cvs = intersectByExpansion(path, le.getValueset(), re.getValueset()); - if (!cvs.getCompose().hasInclude()) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value sets "+lvs.getUrl()+" and "+rvs.getUrl()+" do not intersect", ValidationMessage.IssueSeverity.ERROR)); - status(subset, ProfileUtilities.STATUS_ERROR); - return false; - } - } - } catch (Exception e){ - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "Unable to expand or process value sets "+lvs.getUrl()+" and "+rvs.getUrl()+": "+e.getMessage(), ValidationMessage.IssueSeverity.ERROR)); - status(subset, ProfileUtilities.STATUS_ERROR); - e.printStackTrace(); - return false; - } - } - if (cvs != null) { - subBinding.setValueSet("#"+addValueSet(cvs)); - superBinding.setValueSet("#"+addValueSet(unite(superset, outcome, path, lvs, rvs))); - } - } - } - return false; - } - - private ElementDefinitionBindingComponent unionBindings(ElementDefinition ed, ProfileComparison outcome, String path, ElementDefinitionBindingComponent left, ElementDefinitionBindingComponent right) throws FHIRFormatError { - ElementDefinitionBindingComponent union = new ElementDefinitionBindingComponent(); - if (left.getStrength().compareTo(right.getStrength()) < 0) - union.setStrength(left.getStrength()); - else - union.setStrength(right.getStrength()); - union.setDescription(mergeText(ed, outcome, path, "binding.description", left.getDescription(), right.getDescription())); - if (Base.compareDeep(left.getValueSet(), right.getValueSet(), false)) - union.setValueSet(left.getValueSet()); - else { - ValueSet lvs = resolveVS(outcome.left, left.getValueSet()); - ValueSet rvs = resolveVS(outcome.left, right.getValueSet()); - if (lvs != null && rvs != null) - union.setValueSet("#"+addValueSet(unite(ed, outcome, path, lvs, rvs))); - else if (lvs != null) - union.setValueSet("#"+addValueSet(lvs)); - else if (rvs != null) - union.setValueSet("#"+addValueSet(rvs)); - } - return union; - } - - - private ValueSet unite(ElementDefinition ed, ProfileComparison outcome, String path, ValueSet lvs, ValueSet rvs) { - ValueSet vs = new ValueSet(); - vs.setName(path); - if (lvs.hasCompose()) { - for (ConceptSetComponent inc : lvs.getCompose().getInclude()) - vs.getCompose().getInclude().add(inc); - if (lvs.getCompose().hasExclude()) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value sets "+lvs.getUrl()+" has exclude statements, and no union involving it can be correctly determined", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - } - } - if (rvs.hasCompose()) { - for (ConceptSetComponent inc : rvs.getCompose().getInclude()) - if (!mergeIntoExisting(vs.getCompose().getInclude(), inc)) - vs.getCompose().getInclude().add(inc); - if (rvs.getCompose().hasExclude()) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "The value sets "+lvs.getUrl()+" has exclude statements, and no union involving it can be correctly determined", ValidationMessage.IssueSeverity.ERROR)); - status(ed, ProfileUtilities.STATUS_ERROR); - } - } - return vs; - } - - private boolean mergeIntoExisting(List include, ConceptSetComponent inc) { - for (ConceptSetComponent dst : include) { - if (Base.compareDeep(dst, inc, false)) - return true; // they're actually the same - if (dst.hasSystem() && dst.getSystem().equals(inc.getSystem())) { - if (inc.hasFilter() || dst.hasFilter()) { - return false; // just add the new one as a a parallel - } else if (inc.hasConcept() && dst.hasConcept()) { - for (ConceptReferenceComponent cc : inc.getConcept()) { - boolean found = false; - for (ConceptReferenceComponent dd : dst.getConcept()) { - if (dd.getCode().equals(cc.getCode())) - found = true; - if (found) { - if (cc.hasDisplay() && !dd.hasDisplay()) - dd.setDisplay(cc.getDisplay()); - break; - } - } - if (!found) - dst.getConcept().add(cc.copy()); - } - } else - dst.getConcept().clear(); // one of them includes the entire code system - } - } - return false; - } - - private ValueSet resolveVS(StructureDefinition ctxtLeft, String vsRef) { - if (vsRef == null) - return null; - return context.fetchResource(ValueSet.class, vsRef); - } - - private ValueSet intersectByDefinition(ValueSet lvs, ValueSet rvs) { - // this is just a stub. The idea is that we try to avoid expanding big open value sets from SCT, RxNorm, LOINC. - // there's a bit of long hand logic coming here, but that's ok. - return null; - } - - private ValueSet intersectByExpansion(String path, ValueSet lvs, ValueSet rvs) { - // this is pretty straight forward - we intersect the lists, and build a compose out of the intersection - ValueSet vs = new ValueSet(); - vs.setName(path); - vs.setStatus(PublicationStatus.DRAFT); - - Map left = new HashMap(); - scan(lvs.getExpansion().getContains(), left); - Map right = new HashMap(); - scan(rvs.getExpansion().getContains(), right); - Map inc = new HashMap(); - - for (String s : left.keySet()) { - if (right.containsKey(s)) { - ValueSetExpansionContainsComponent cc = left.get(s); - ConceptSetComponent c = inc.get(cc.getSystem()); - if (c == null) { - c = vs.getCompose().addInclude().setSystem(cc.getSystem()); - inc.put(cc.getSystem(), c); - } - c.addConcept().setCode(cc.getCode()).setDisplay(cc.getDisplay()); - } - } - return vs; - } - - private void scan(List list, Map map) { - for (ValueSetExpansionContainsComponent cc : list) { - if (cc.hasSystem() && cc.hasCode()) { - String s = cc.getSystem()+"::"+cc.getCode(); - if (!map.containsKey(s)) - map.put(s, cc); - } - if (cc.hasContains()) - scan(cc.getContains(), map); - } - } - - private boolean closed(ValueSet vs) { - return !ToolingExtensions.findBooleanExtension(vs.getExpansion(), ToolingExtensions.EXT_UNCLOSED); - } - - private boolean isPreferredOrExample(ElementDefinitionBindingComponent binding) { - return binding.getStrength() == BindingStrength.EXAMPLE || binding.getStrength() == BindingStrength.PREFERRED; - } - - private Collection intersectTypes(ElementDefinition ed, ProfileComparison outcome, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { - List result = new ArrayList(); - for (TypeRefComponent l : left) { - if (l.hasAggregation()) - throw new DefinitionException("Aggregation not supported: "+path); - boolean pfound = false; - boolean tfound = false; - TypeRefComponent c = l.copy(); - for (TypeRefComponent r : right) { - if (r.hasAggregation()) - throw new DefinitionException("Aggregation not supported: "+path); - if (!l.hasProfile() && !r.hasProfile()) { - pfound = true; - } else if (!r.hasProfile()) { - pfound = true; - } else if (!l.hasProfile()) { - pfound = true; - c.setProfile(r.getProfile()); - } else { - StructureDefinition sdl = resolveProfile(ed, outcome, path, l.getProfile().get(0).getValue(), outcome.leftName()); - StructureDefinition sdr = resolveProfile(ed, outcome, path, r.getProfile().get(0).getValue(), outcome.rightName()); - if (sdl != null && sdr != null) { - if (sdl == sdr) { - pfound = true; - } else if (derivesFrom(sdl, sdr)) { - pfound = true; - } else if (derivesFrom(sdr, sdl)) { - c.setProfile(r.getProfile()); - pfound = true; - } else if (sdl.getType().equals(sdr.getType())) { - ProfileComparison comp = compareProfiles(sdl, sdr); - if (comp.getSubset() != null) { - pfound = true; - c.addProfile("#"+comp.id); - } - } - } - } - if (!l.hasTargetProfile() && !r.hasTargetProfile()) { - tfound = true; - } else if (!r.hasTargetProfile()) { - tfound = true; - } else if (!l.hasTargetProfile()) { - tfound = true; - c.setTargetProfile(r.getTargetProfile()); - } else { - StructureDefinition sdl = resolveProfile(ed, outcome, path, l.getTargetProfile().get(0).getValue(), outcome.leftName()); - StructureDefinition sdr = resolveProfile(ed, outcome, path, r.getTargetProfile().get(0).getValue(), outcome.rightName()); - if (sdl != null && sdr != null) { - if (sdl == sdr) { - tfound = true; - } else if (derivesFrom(sdl, sdr)) { - tfound = true; - } else if (derivesFrom(sdr, sdl)) { - c.setTargetProfile(r.getTargetProfile()); - tfound = true; - } else if (sdl.getType().equals(sdr.getType())) { - ProfileComparison comp = compareProfiles(sdl, sdr); - if (comp.getSubset() != null) { - tfound = true; - c.addTargetProfile("#"+comp.id); - } - } - } - } - } - if (pfound && tfound) - result.add(c); - } - return result; - } - - private StructureDefinition resolveProfile(ElementDefinition ed, ProfileComparison outcome, String path, String url, String name) { - StructureDefinition res = context.fetchResource(StructureDefinition.class, url); - if (res == null) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.INFORMATIONAL, path, "Unable to resolve profile "+url+" in profile "+name, ValidationMessage.IssueSeverity.WARNING)); - status(ed, ProfileUtilities.STATUS_HINT); - } - return res; - } - - private Collection unionTypes(String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { - List result = new ArrayList(); - for (TypeRefComponent l : left) - checkAddTypeUnion(path, result, l); - for (TypeRefComponent r : right) - checkAddTypeUnion(path, result, r); - return result; - } - - private void checkAddTypeUnion(String path, List results, TypeRefComponent nw) throws DefinitionException, IOException, FHIRFormatError { - boolean pfound = false; - boolean tfound = false; - nw = nw.copy(); - if (nw.hasAggregation()) - throw new DefinitionException("Aggregation not supported: "+path); - for (TypeRefComponent ex : results) { - if (Utilities.equals(ex.getWorkingCode(), nw.getWorkingCode())) { - if (!ex.hasProfile() && !nw.hasProfile()) - pfound = true; - else if (!ex.hasProfile()) { - pfound = true; - } else if (!nw.hasProfile()) { - pfound = true; - ex.setProfile(null); - } else { - // both have profiles. Is one derived from the other? - StructureDefinition sdex = context.fetchResource(StructureDefinition.class, ex.getProfile().get(0).getValue()); - StructureDefinition sdnw = context.fetchResource(StructureDefinition.class, nw.getProfile().get(0).getValue()); - if (sdex != null && sdnw != null) { - if (sdex == sdnw) { - pfound = true; - } else if (derivesFrom(sdex, sdnw)) { - ex.setProfile(nw.getProfile()); - pfound = true; - } else if (derivesFrom(sdnw, sdex)) { - pfound = true; - } else if (sdnw.getSnapshot().getElement().get(0).getPath().equals(sdex.getSnapshot().getElement().get(0).getPath())) { - ProfileComparison comp = compareProfiles(sdex, sdnw); - if (comp.getSuperset() != null) { - pfound = true; - ex.addProfile("#"+comp.id); - } - } - } - } - if (!ex.hasTargetProfile() && !nw.hasTargetProfile()) - tfound = true; - else if (!ex.hasTargetProfile()) { - tfound = true; - } else if (!nw.hasTargetProfile()) { - tfound = true; - ex.setTargetProfile(null); - } else { - // both have profiles. Is one derived from the other? - StructureDefinition sdex = context.fetchResource(StructureDefinition.class, ex.getTargetProfile().get(0).getValue()); - StructureDefinition sdnw = context.fetchResource(StructureDefinition.class, nw.getTargetProfile().get(0).getValue()); - if (sdex != null && sdnw != null) { - if (sdex == sdnw) { - tfound = true; - } else if (derivesFrom(sdex, sdnw)) { - ex.setTargetProfile(nw.getTargetProfile()); - tfound = true; - } else if (derivesFrom(sdnw, sdex)) { - tfound = true; - } else if (sdnw.getSnapshot().getElement().get(0).getPath().equals(sdex.getSnapshot().getElement().get(0).getPath())) { - ProfileComparison comp = compareProfiles(sdex, sdnw); - if (comp.getSuperset() != null) { - tfound = true; - ex.addTargetProfile("#"+comp.id); - } - } - } - } - } - } - if (!tfound || !pfound) - results.add(nw); - } - - - private boolean derivesFrom(StructureDefinition left, StructureDefinition right) { - // left derives from right if it's base is the same as right - // todo: recursive... - return left.hasBaseDefinition() && left.getBaseDefinition().equals(right.getUrl()); - } - - - private String mergeText(ElementDefinition ed, ProfileComparison outcome, String path, String name, String left, String right) { - if (left == null && right == null) - return null; - if (left == null) - return right; - if (right == null) - return left; - left = stripLinks(left); - right = stripLinks(right); - if (left.equalsIgnoreCase(right)) - return left; - if (path != null) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.INFORMATIONAL, path, "Elements differ in definition for "+name+":\r\n \""+left+"\"\r\n \""+right+"\"", - "Elements differ in definition for "+name+":
\""+Utilities.escapeXml(left)+"\"
\""+Utilities.escapeXml(right)+"\"", ValidationMessage.IssueSeverity.INFORMATION)); - status(ed, ProfileUtilities.STATUS_HINT); - } - return "left: "+left+"; right: "+right; - } - - - private String stripLinks(String s) { - while (s.contains("](")) { - int i = s.indexOf("]("); - int j = s.substring(i).indexOf(")"); - if (j == -1) - return s; - else - s = s.substring(0, i+1)+s.substring(i+j+1); - } - return s; - } - - private List mergeCodings(List left, List right) { - List result = new ArrayList(); - result.addAll(left); - for (Coding c : right) { - boolean found = false; - for (Coding ct : left) - if (Utilities.equals(c.getSystem(), ct.getSystem()) && Utilities.equals(c.getCode(), ct.getCode())) - found = true; - if (!found) - result.add(c); - } - return result; - } - - private List mergeStrings(List left, List right) { - List result = new ArrayList(); - result.addAll(left); - for (StringType c : right) { - boolean found = false; - for (StringType ct : left) - if (Utilities.equals(c.getValue(), ct.getValue())) - found = true; - if (!found) - result.add(c); - } - return result; - } - - private List mergeMappings(List left, List right) { - List result = new ArrayList(); - result.addAll(left); - for (ElementDefinitionMappingComponent c : right) { - boolean found = false; - for (ElementDefinitionMappingComponent ct : left) - if (Utilities.equals(c.getIdentity(), ct.getIdentity()) && Utilities.equals(c.getLanguage(), ct.getLanguage()) && Utilities.equals(c.getMap(), ct.getMap())) - found = true; - if (!found) - result.add(c); - } - return result; - } - - // we can't really know about constraints. We create warnings, and collate them - private List unionConstraints(ElementDefinition ed, ProfileComparison outcome, String path, List left, List right) { - List result = new ArrayList(); - for (ElementDefinitionConstraintComponent l : left) { - boolean found = false; - for (ElementDefinitionConstraintComponent r : right) - if (Utilities.equals(r.getId(), l.getId()) || (Utilities.equals(r.getXpath(), l.getXpath()) && r.getSeverity() == l.getSeverity())) - found = true; - if (!found) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "StructureDefinition "+outcome.leftName()+" has a constraint that is not found in "+outcome.rightName()+" and it is uncertain whether they are compatible ("+l.getXpath()+")", ValidationMessage.IssueSeverity.INFORMATION)); - status(ed, ProfileUtilities.STATUS_WARNING); - } - result.add(l); - } - for (ElementDefinitionConstraintComponent r : right) { - boolean found = false; - for (ElementDefinitionConstraintComponent l : left) - if (Utilities.equals(r.getId(), l.getId()) || (Utilities.equals(r.getXpath(), l.getXpath()) && r.getSeverity() == l.getSeverity())) - found = true; - if (!found) { - outcome.messages.add(new ValidationMessage(Source.ProfileComparer, ValidationMessage.IssueType.STRUCTURE, path, "StructureDefinition "+outcome.rightName()+" has a constraint that is not found in "+outcome.leftName()+" and it is uncertain whether they are compatible ("+r.getXpath()+")", ValidationMessage.IssueSeverity.INFORMATION)); - status(ed, ProfileUtilities.STATUS_WARNING); - result.add(r); - } - } - return result; - } - - - private List intersectConstraints(String path, List left, List right) { - List result = new ArrayList(); - for (ElementDefinitionConstraintComponent l : left) { - boolean found = false; - for (ElementDefinitionConstraintComponent r : right) - if (Utilities.equals(r.getId(), l.getId()) || (Utilities.equals(r.getXpath(), l.getXpath()) && r.getSeverity() == l.getSeverity())) - found = true; - if (found) - result.add(l); - } - return result; -} - - private String card(DefinitionNavigator defn) { - return Integer.toString(defn.current().getMin())+".."+defn.current().getMax(); - } - - private String typeCode(DefinitionNavigator defn) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (TypeRefComponent t : defn.current().getType()) - b.append(t.getWorkingCode()+(t.hasProfile() ? "("+t.getProfile()+")" : "")+(t.hasTargetProfile() ? "("+t.getTargetProfile()+")" : "")); // todo: other properties - return b.toString(); - } - - private int intersectMin(int left, int right) { - if (left > right) - return left; - else - return right; - } - - private int unionMin(int left, int right) { - if (left > right) - return right; - else - return left; - } - - private String intersectMax(String left, String right) { - int l = "*".equals(left) ? Integer.MAX_VALUE : Integer.parseInt(left); - int r = "*".equals(right) ? Integer.MAX_VALUE : Integer.parseInt(right); - if (l < r) - return left; - else - return right; - } - - private String unionMax(String left, String right) { - int l = "*".equals(left) ? Integer.MAX_VALUE : Integer.parseInt(left); - int r = "*".equals(right) ? Integer.MAX_VALUE : Integer.parseInt(right); - if (l < r) - return right; - else - return left; - } - - private IntegerType intersectMaxLength(int left, int right) { - if (left == 0) - left = Integer.MAX_VALUE; - if (right == 0) - right = Integer.MAX_VALUE; - if (left < right) - return left == Integer.MAX_VALUE ? null : new IntegerType(left); - else - return right == Integer.MAX_VALUE ? null : new IntegerType(right); - } - - private IntegerType unionMaxLength(int left, int right) { - if (left == 0) - left = Integer.MAX_VALUE; - if (right == 0) - right = Integer.MAX_VALUE; - if (left < right) - return right == Integer.MAX_VALUE ? null : new IntegerType(right); - else - return left == Integer.MAX_VALUE ? null : new IntegerType(left); - } - - - public String addValueSet(ValueSet cvs) { - String id = Integer.toString(valuesets.size()+1); - cvs.setId(id); - valuesets.add(cvs); - return id; - } - - - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getLeftLink() { - return leftLink; - } - - public void setLeftLink(String leftLink) { - this.leftLink = leftLink; - } - - public String getLeftName() { - return leftName; - } - - public void setLeftName(String leftName) { - this.leftName = leftName; - } - - public String getRightLink() { - return rightLink; - } - - public void setRightLink(String rightLink) { - this.rightLink = rightLink; - } - - public String getRightName() { - return rightName; - } - - public void setRightName(String rightName) { - this.rightName = rightName; - } - - public String getLeftPrefix() { - return leftPrefix; - } - - public void setLeftPrefix(String leftPrefix) { - this.leftPrefix = leftPrefix; - } - - public String getRightPrefix() { - return rightPrefix; - } - - public void setRightPrefix(String rightPrefix) { - this.rightPrefix = rightPrefix; - } - - private String genPCLink(String name, String link, String prefix) { - if (link == null) { - return name; - } else if (!Utilities.isAbsoluteUrl(link) && !Utilities.noString(prefix)) { - return ""+Utilities.escapeXml(name)+""; - } else { - return ""+Utilities.escapeXml(name)+""; - } - } - - private String genValueSets(String base) throws IOException, FHIRException, EOperationOutcome { - StringBuilder b = new StringBuilder(); - b.append("
    \r\n"); - for (ValueSet vs : getValuesets()) { - System.out.println(" .. Value set: "+vs.getName()); - b.append("
  • "); - b.append("
"); - b.append("\r\n"); - genValueSetFile(base+"-"+vs.getId()+".html", vs); - } - b.append("\r\n"); - return b.toString(); - } - - private void genValueSetFile(String filename, ValueSet vs) throws IOException, FHIRException, EOperationOutcome { - RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE); - rc.setNoSlowLookup(true); - RendererFactory.factory(vs, rc).render(vs); - String s = new XhtmlComposer(XhtmlComposer.HTML).compose(vs.getText().getDiv()); - StringBuilder b = new StringBuilder(); - b.append(""); - b.append(""); - b.append(""+vs.present()+""); - b.append("\r\n"); - b.append(""); - b.append(""); - b.append("

"+vs.present()+"

"); - b.append(s); - b.append(""); - b.append(""); - TextFile.stringToFile(b.toString(), filename); - } - - private String genPCTable() { - StringBuilder b = new StringBuilder(); - - b.append("
"+Utilities.escapeXml(leftName)+""+Utilities.escapeXml(rightName)+"DifferenceNotes
"+name+"
"+Utilities.pluralize(name, 2)+"
"+Utilities.escapeXml(t)+""+Utilities.escapeXml(t)+""+left.getUrl()+""+right.getUrl()+""+left.getUrl()+""+right.getUrl()+"Added
"+Utilities.escapeXml(vs.present())+"
\r\n"); - b.append(""); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(""); - - for (ProfileComparison cmp : getComparisons()) { - b.append(""); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(" "); - b.append(""); - } - b.append("
LeftRightComparisonError #Warning #Hint #
"+Utilities.escapeXml(cmp.getLeft().getName())+""+Utilities.escapeXml(cmp.getRight().getName())+"Click Here"+cmp.getErrorCount()+""+cmp.getWarningCount()+""+cmp.getHintCount()+"
\r\n"); - - return b.toString(); - } - - - private String fixLink(String path, String pfx) { - return (pfx == null || Utilities.isAbsoluteUrl(path)) ? path : Utilities.pathURL(pfx, path); - } - - private String genCmpMessages(ProfileComparison cmp) { - StringBuilder b = new StringBuilder(); - b.append("\r\n"); - b.append("\r\n"); - b.append("\r\n"); - boolean found = false; - for (ValidationMessage vm : cmp.getMessages()) - if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) { - found = true; - b.append("\r\n"); - } - if (!found) - b.append("\r\n"); - - boolean first = true; - for (ValidationMessage vm : cmp.getMessages()) - if (vm.getLevel() == IssueSeverity.WARNING) { - if (first) { - first = false; - b.append("\r\n"); - } - b.append("\r\n"); - } - first = true; - for (ValidationMessage vm : cmp.getMessages()) - if (vm.getLevel() == IssueSeverity.INFORMATION) { - if (first) { - b.append("\r\n"); - first = false; - } - b.append("\r\n"); - } - b.append("
PathMessage
Errors Detected
"+vm.getLocation()+""+vm.getHtml()+(vm.getLevel() == IssueSeverity.FATAL ? "(This error terminated the comparison process)" : "")+"
(None)
Warnings about the comparison
"+vm.getLocation()+""+vm.getHtml()+"
Notes about differences (e.g. definitions)
"+vm.getLocation()+""+vm.getHtml()+"
\r\n"); - return b.toString(); - } - - private String genCompModel(StructureDefinition sd, String name, String base, String prefix, String dest) throws FHIRException, IOException { - if (sd == null) - return "

No "+name+" could be generated

\r\n"; - return new XhtmlComposer(XhtmlComposer.HTML).compose(new ProfileUtilities(context, null, this).generateTable("?gen-cm?", sd, false, dest, false, base, true, prefix, prefix, false, false, null, true)); - } - - - public String generate() throws IOException, FHIRException, EOperationOutcome { - for (ValueSet vs : valuesets) { - vs.setUserData("path", folder+"/"+getId()+"-vs-"+vs.getId()+".html"); - } - // ok, all compared; now produce the output - // first page we produce is simply the index - Map vars = new HashMap(); - vars.put("title", getTitle()); - vars.put("left", genPCLink(getLeftName(), getLeftLink(), getLeftPrefix())); - vars.put("right", genPCLink(getRightName(), getRightLink(), getRightPrefix())); - vars.put("table", genPCTable()); - vars.put("valuesets", genValueSets(folder+"/"+getId()+"-vs")); - producePage(summaryTemplate(), Utilities.path(folder, getId()+".html"), vars); - - // then we produce a comparison page for each pair - for (ProfileComparison cmp : getComparisons()) { - vars.clear(); - vars.put("title", getTitle()); - vars.put("left", genPCLink(getLeftName(), getLeftLink(), getLeftPrefix())); - vars.put("right", genPCLink(getRightName(), getRightLink(), getRightPrefix())); - vars.put("messages", genCmpMessages(cmp)); - vars.put("subset", genCompModel(cmp.getSubset(), "intersection", getId()+"."+cmp.getId(), "", folder)); - vars.put("superset", genCompModel(cmp.getSuperset(), "union", getId()+"."+cmp.getId(), "", folder)); - producePage(singleTemplate(), Utilities.path(folder, getId()+"."+cmp.getId()+".html"), vars); - } -// // and also individual pages for each pair outcome -// // then we produce value set pages for each value set -// -// // TODO Auto-generated method stub - return Utilities.path(folder, getId()+".html"); - } - - - private void producePage(String src, String path, Map vars) throws IOException { - while (src.contains("[%")) - { - int i1 = src.indexOf("[%"); - int i2 = src.substring(i1).indexOf("%]")+i1; - String s1 = src.substring(0, i1); - String s2 = src.substring(i1 + 2, i2).trim(); - String s3 = src.substring(i2+2); - String v = vars.containsKey(s2) ? vars.get(s2) : "?pp??"; - src = s1+v+s3; - } - TextFile.stringToFile(src, path); - } - - private String summaryTemplate() throws IOException { - return TextFile.fileToString(Utilities.path(folder, "template-comparison-set.html")); - } - - private String singleTemplate() throws IOException { - return TextFile.fileToString(Utilities.path(folder, "template-comparison.html")); - } - - private String cachedFetch(String id, String source) throws IOException { - String tmpDir = System.getProperty("java.io.tmpdir"); - String local = Utilities.path(tmpDir, id); - File f = new File(local); - if (f.exists()) - return TextFile.fileToString(f); - URL url = new URL(source); - URLConnection c = url.openConnection(); - String result = TextFile.streamToString(c.getInputStream()); - TextFile.stringToFile(result, f); - return result; - } - - @Override - public boolean isDatatype(String typeSimple) { - throw new Error("Not done yet"); - } - - @Override - public boolean isResource(String typeSimple) { - throw new Error("Not done yet"); - } - - @Override - public boolean hasLinkFor(String name) { - StructureDefinition sd = context.fetchTypeDefinition(name); - return sd != null && sd.hasUserData("path"); - } - - @Override - public String getLinkFor(String corePath, String name) { - StructureDefinition sd = context.fetchTypeDefinition(name); - return sd == null ? null : sd.getUserString("path"); - } - - @Override - public BindingResolution resolveBinding(StructureDefinition def, ElementDefinitionBindingComponent binding, String path) throws FHIRException { - return resolveBindingInt(def, binding.getValueSet(), binding.getDescription()); - } - - @Override - public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException { - return resolveBindingInt(def, url, url); - } - - public BindingResolution resolveBindingInt(StructureDefinition def, String url, String desc) throws FHIRException { - ValueSet vs = null; - if (url != null && url.startsWith("#")) { - for (ValueSet t : valuesets) { - if (("#"+t.getId()).equals(url)) { - vs = t; - break; - } - } - } - if (url != null && vs == null) - context.fetchResource(ValueSet.class, url); - BindingResolution br = new BindingResolution(); - if (vs != null) { - br.display = vs.present(); - br.url = vs.getUserString("path"); - } else { - br.display = desc; - } - return br; - } - - @Override - public String getLinkForProfile(StructureDefinition profile, String url) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - return sd == null ? null : sd.getUserString("path")+"|"+sd.present(); - } - - @Override - public boolean prependLinks() { - return false; - } - - @Override - public String getLinkForUrl(String corePath, String s) { - return null; - } - - public int getErrCount() { - int res = 0; - for (ProfileComparison pc : comparisons) { - res = res + pc.getErrorCount(); - } - return res; - - } - - - - -} \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 5ee1f033b..f4ce4522b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -3615,14 +3615,18 @@ public class ProfileUtilities extends TranslatingUtilities { Cell gc = gen.new Cell(); row.getCells().add(gc); res.add(gc); - if (element != null && element.getIsModifier()) + if (element != null && element.getIsModifier()) { checkForNoChange(element.getIsModifierElement(), gc.addStyledText(translate("sd.table", "This element is a modifier element"), "?!", null, null, null, false)); - if (element != null && element.getMustSupport()) + } + if (element != null && element.getMustSupport()) { checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false)); - if (element != null && element.getIsSummary()) + } + if (element != null && element.getIsSummary()) { checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false)); - if (element != null && (!element.getConstraint().isEmpty() || !element.getCondition().isEmpty())) + } + if (element != null && (!element.getConstraint().isEmpty() || !element.getCondition().isEmpty())) { gc.addStyledText(translate("sd.table", "This element has or is affected by some invariants ("+listConstraintsAndConditions(element)+")"), "I", null, null, null, false); + } ExtensionContext extDefn = null; if (ext) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index d8112ff25..d66cee01c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -437,7 +437,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon if (types.length == 0) { types = new String[] { "StructureDefinition", "ValueSet", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem" }; } - for (String s : pi.listResources(loader.getTypes())) { + for (String s : pi.listResources(types)) { try { loadDefinitionItem(s, pi.load("package", s), loader, null, new PackageVersion(pi.id(), pi.version())); t++; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java index d026fcab5..7e51886a1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java @@ -125,8 +125,9 @@ public class DiagnosticReportRenderer extends ResourceRenderer { } pw = getProperty(dr, "conclusion"); - if (valued(pw)) + if (valued(pw)) { render(x.para(), pw.value()); + } pw = getProperty(dr, "conclusionCode"); if (!valued(pw)) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/KeyGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/KeyGenerator.java deleted file mode 100644 index 06fdb4584..000000000 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/KeyGenerator.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.hl7.fhir.r5.utils; - -import org.hl7.fhir.r5.model.CanonicalResource; -import org.hl7.fhir.r5.model.DomainResource; -import org.hl7.fhir.utilities.Utilities; - -public class KeyGenerator { - - private String canonical; - int i = 0; - - public KeyGenerator(String canonical) { - super(); - this.canonical = canonical; - } - - public void genId(DomainResource dr) { - i++; - dr.setId(getAcronym(dr.fhirType())+Integer.toString(i)); - if (dr instanceof CanonicalResource) { - CanonicalResource mr = (CanonicalResource) dr; - mr.setUrl(Utilities.pathURL(canonical, mr.fhirType(), mr.getId())); - } - } - - private String getAcronym(String rt) { - if ("CapabilityStatement".equals(rt)) { - return "cs"; - } - if ("ValueSet".equals(rt)) { - return "vs"; - } - if ("CodeSystem".equals(rt)) { - return "cs"; - } - if ("StructureDefinition".equals(rt)) { - return "sd"; - } - if ("StructureMap".equals(rt)) { - return "sm"; - } - if ("ConceptMap".equals(rt)) { - return "cm"; - } - if ("ImplementationGuide".equals(rt)) { - return ""; - } - if ("SearchParameter".equals(rt)) { - return "sp"; - } - if ("MessageDefinition".equals(rt)) { - return "md"; - } - if ("OperationDefinition".equals(rt)) { - return "od"; - } - if ("CompartmentDefinition".equals(rt)) { - return "cd"; - } - if ("GraphDefinition".equals(rt)) { - return "gd"; - } - if ("ExampleScenario".equals(rt)) { - return "es"; - } - if ("NamingSystem".equals(rt)) { - return "ns"; - } - if ("TerminologyCapabilities".equals(rt)) { - return "tc"; - } - return "r"; - } - -} \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java index 6fab3a623..75d298867 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java @@ -1080,12 +1080,15 @@ public class NpmPackage { return false; } - public boolean canLazyLoad() { + public boolean canLazyLoad() throws IOException { for (NpmPackageFolder folder : folders.values()) { if (folder.folder == null) { return false; } } + if (!hasFile("other", "spec.internals")) { + return false; + } return true; } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java index b9b7bb05c..62ad4dd1c 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java @@ -25,7 +25,7 @@ import com.google.gson.JsonObject; public class PackageHacker { public static void main(String[] args) throws FileNotFoundException, IOException { - new PackageHacker().edit("M:\\web\\terminology.hl7.org\\hl7.terminology.r3.tgz"); + new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\us\\davinci-pdex-plan-net\\2020Feb\\package.tgz"); } private void edit(String name) throws FileNotFoundException, IOException { @@ -57,16 +57,17 @@ public class PackageHacker { private void change(JsonObject npm, Map content) throws FileNotFoundException, IOException { // fixVersions(npm); -// npm.remove("name"); -// npm.addProperty("name", "hl7.terminology.r5"); - npm.remove("version"); - npm.addProperty("version", "1.0.0"); +// npm.remove("url"); +// npm.addProperty("url", url); +// npm.remove("version"); +// npm.addProperty("version", "1.0.0"); // npm.remove("canonical"); // npm.addProperty("canonical", "http://hl7.org/fhir/us/davinci-drug-formulary"); //// npm.remove("description"); //// npm.addProperty("description", "Group Wrapper that includes all the R4 packages"); // npm.remove("url"); // npm.addProperty("url", "https://terminology.hl7.org/1.0.0/"); +// npm.remove("dependencies"); // JsonObject dep = new JsonObject(); // npm.add("dependencies", dep); // dep.addProperty("hl7.fhir.r4.core", "4.0.1"); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ComparisonService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ComparisonService.java index 8b55587c5..30afb9c22 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ComparisonService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ComparisonService.java @@ -2,7 +2,6 @@ package org.hl7.fhir.validation.cli.services; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities; -import org.hl7.fhir.r5.conformance.ProfileComparer; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; @@ -11,7 +10,6 @@ import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.utils.EOperationOutcome; -import org.hl7.fhir.r5.utils.KeyGenerator; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -54,26 +52,26 @@ public class ComparisonService { String nameLeft = chooseName(args, "leftName", resLeft); String nameRight = chooseName(args, "rightName", resRight); System.out.println("Comparing CapabilityStatements " + left + " to " + right); - CapabilityStatementUtilities pc = new CapabilityStatementUtilities(validator.getContext(), dest, new KeyGenerator("http://fhir.org/temp/" + UUID.randomUUID().toString().toLowerCase())); - CapabilityStatement capL = (CapabilityStatement) resLeft; - CapabilityStatement capR = (CapabilityStatement) resRight; - CapabilityStatementUtilities.CapabilityStatementComparisonOutput output = pc.isCompatible(nameLeft, nameRight, capL, capR); - - String destTxt = Utilities.path(dest, "output.txt"); - System.out.println("Generating output to " + destTxt + "..."); - StringBuilder b = new StringBuilder(); - for (ValidationMessage msg : output.getMessages()) { - b.append(msg.summary()); - b.append("\r\n"); - } - TextFile.stringToFile(b.toString(), destTxt); - new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.xml")), output.getSuperset()); - new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.xml")), output.getSubset()); - new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.xml")), output.getOutcome()); - new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.json")), output.getSuperset()); - new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.json")), output.getSubset()); - new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.json")), output.getOutcome()); - +// CapabilityStatementUtilities pc = new CapabilityStatementUtilities(validator.getContext(), dest, new KeyGenerator("http://fhir.org/temp/" + UUID.randomUUID().toString().toLowerCase())); +// CapabilityStatement capL = (CapabilityStatement) resLeft; +// CapabilityStatement capR = (CapabilityStatement) resRight; +// CapabilityStatementUtilities.CapabilityStatementComparisonOutput output = pc.isCompatible(nameLeft, nameRight, capL, capR); +// +// String destTxt = Utilities.path(dest, "output.txt"); +// System.out.println("Generating output to " + destTxt + "..."); +// StringBuilder b = new StringBuilder(); +// for (ValidationMessage msg : output.getMessages()) { +// b.append(msg.summary()); +// b.append("\r\n"); +// } +// TextFile.stringToFile(b.toString(), destTxt); +// new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.xml")), output.getSuperset()); +// new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.xml")), output.getSubset()); +// new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.xml")), output.getOutcome()); +// new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.json")), output.getSuperset()); +// new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.json")), output.getSubset()); +// new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.json")), output.getOutcome()); +// String destHtml = Utilities.path(dest, "public/index.html"); File htmlFile = new File(destHtml); Desktop.getDesktop().browse(htmlFile.toURI()); @@ -81,15 +79,15 @@ public class ComparisonService { } public static void compareStructureDefinitions(String dest, ValidationEngine validator, String left, String right, StructureDefinition resLeft, StructureDefinition resRight) throws IOException, FHIRException, EOperationOutcome { - System.out.println("Comparing StructureDefinitions " + left + " to " + right); - ProfileComparer pc = new ProfileComparer(validator.getContext(), dest); - StructureDefinition sdL = resLeft; - StructureDefinition sdR = resRight; - pc.compareProfiles(sdL, sdR); - System.out.println("Generating output to " + dest + "..."); - File htmlFile = new File(pc.generate()); - Desktop.getDesktop().browse(htmlFile.toURI()); - System.out.println("Done"); +// System.out.println("Comparing StructureDefinitions " + left + " to " + right); +// ProfileComparer pc = new ProfileComparer(validator.getContext(), dest); +// StructureDefinition sdL = resLeft; +// StructureDefinition sdR = resRight; +// pc.compareProfiles(sdL, sdR); +// System.out.println("Generating output to " + dest + "..."); +// File htmlFile = new File(pc.generate()); +// Desktop.getDesktop().browse(htmlFile.toURI()); +// System.out.println("Done"); } private static String chooseName(String[] args, String name, CanonicalResource mr) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 0b97f42b9..744a00ec7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -4527,10 +4527,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat msg = ex.getMessage(); } if (!ok) { - if (!Utilities.noString(msg)) + if (!Utilities.noString(msg)) { msg = " (" + msg + ")"; - if (debug) { - System.out.println(" failed! "+msg); } if (inv.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice") && ToolingExtensions.readBooleanExtension(inv, "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice")) {