diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExtensionExtractor.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExtensionExtractor.java index ac3bd2467..a5a9b9421 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExtensionExtractor.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExtensionExtractor.java @@ -5,6 +5,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.context.CanonicalResourceManager; @@ -21,17 +23,21 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.json.model.JsonObject; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.xml.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; public class ExtensionExtractor { public static void main(String[] args) throws FHIRFormatError, FileNotFoundException, IOException { new ExtensionExtractor().process(args[0]); - } private void process(String dst) throws IOException { + Set ids = new HashSet<>(); FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true); NpmPackage r5 = pcm.loadPackage("hl7.fhir.r5.core", "current"); CanonicalResourceManager cslist = new CanonicalResourceManager(true); @@ -67,7 +73,7 @@ public class ExtensionExtractor { sd.setSnapshot(null); String fn; if (sd.getContext().size() == 0) { - save(sd, dst,"none"); + save(sd, dst,"none", ids); } else if (sd.getContext().size() > 1) { boolean dt = true; for (StructureDefinitionContextComponent x : sd.getContext()) { @@ -75,16 +81,16 @@ public class ExtensionExtractor { dt = dt && isDataType(s); } if (dt) { - save(sd, dst,"datatypes"); + save(sd, dst,"datatypes", ids); } else { - save(sd, dst,"multiple"); + save(sd, dst,"multiple", ids); } } else { String s = extractType(sd.getContextFirstRep().getExpression()); if (isDataType(s)) { - save(sd, dst,"datatypes"); + save(sd, dst,"datatypes", ids); } else { - save(sd, dst,s); + save(sd, dst,s, ids); } } } else { @@ -118,21 +124,53 @@ public class ExtensionExtractor { for (ValueSet vs : vslist.getList()) { StructureDefinition sd = (StructureDefinition) vs.getUserData("ext"); String s = sd.getUserString("folder"); - save(vs, dst, s); + save(vs, dst, s, ids); } for (CodeSystem cs : cslist.getList()) { ValueSet vs = ((ArrayList) cs.getUserData("vsl")).get(0); String s = vs.getUserString("folder"); - save(cs, dst,s); + save(cs, dst,s, ids); } + + deleteMatchingResources(ids, new File("/Users/grahamegrieve/work/r5/source")); } - private void save(CanonicalResource cr, String dst, String folder) throws IOException { + private void deleteMatchingResources(Set ids, File folder) { + for (File f : folder.listFiles()) { + if (f.isDirectory()) { + deleteMatchingResources(ids, f); + } else if (f.getName().endsWith(".json")) { + try { + JsonObject json = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(f); + if (json.has("resourceType") && json.has("id") && ids.contains(json.asString("id"))) { + System.out.println("Delete "+f.getAbsolutePath()); + f.delete(); + } + } catch (Exception e) { + // nothing + } + } else if (f.getName().endsWith(".xml")) { + try { + Element xml = XMLUtil.parseFileToDom(f.getAbsolutePath()).getDocumentElement(); + if (XMLUtil.hasNamedChild(xml, "id") && ids.contains(XMLUtil.getNamedChildValue(xml, "id"))) { + System.out.println("Delete "+f.getAbsolutePath()); + f.delete(); + } + } catch (Exception e) { + // nothing + } + } + } + + } + + private void save(CanonicalResource cr, String dst, String folder, Set ids) throws IOException { // TODO Auto-generated method stub cr.setText(null); if (!cr.hasTitle()) { cr.setTitle(Utilities.unCamelCase(cr.getName())); } + ids.add(cr.getId()); String fn = Utilities.path(dst, folder, cr.fhirType()+"-"+cr.getId()+".xml"); cr.setUserData("folder", folder); if (!new File(fn).exists()) { diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java index 5e55e6401..b38574ff6 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java @@ -61,6 +61,7 @@ import org.hl7.fhir.utilities.MergedList; import org.hl7.fhir.utilities.MergedList.MergeNode; import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.xhtml.NodeType; @@ -260,6 +261,8 @@ public class FHIRPathEngine { private boolean allowPolymorphicNames; private boolean doImplicitStringConversion; private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host + private boolean doNotEnforceAsSingletonRule; + private boolean doNotEnforceAsCaseSensitive; // if the fhir path expressions are allowed to use constants beyond those defined in the specification // the application can implement them by providing a constant resolver @@ -369,8 +372,15 @@ public class FHIRPathEngine { primitiveTypes.add(sd.getName()); } } + initFlags(); } + private void initFlags() { + if (!VersionUtilities.isR5VerOrLater(worker.getVersion())) { + doNotEnforceAsCaseSensitive = true; + doNotEnforceAsSingletonRule = true; + } + } // --- 3 methods to override in children ------------------------------------------------------- // if you don't override, it falls through to the using the base reference implementation @@ -453,6 +463,22 @@ public class FHIRPathEngine { this.doImplicitStringConversion = doImplicitStringConversion; } + public boolean isDoNotEnforceAsSingletonRule() { + return doNotEnforceAsSingletonRule; + } + + public void setDoNotEnforceAsSingletonRule(boolean doNotEnforceAsSingletonRule) { + this.doNotEnforceAsSingletonRule = doNotEnforceAsSingletonRule; + } + + public boolean isDoNotEnforceAsCaseSensitive() { + return doNotEnforceAsCaseSensitive; + } + + public void setDoNotEnforceAsCaseSensitive(boolean doNotEnforceAsCaseSensitive) { + this.doNotEnforceAsCaseSensitive = doNotEnforceAsCaseSensitive; + } + // --- public API ------------------------------------------------------- /** * Parse a path for later use using execute @@ -1789,11 +1815,11 @@ public class FHIRPathEngine { if (!isKnownType(tn)) { throw new PathEngineException("The type "+tn+" is not valid"); } - if (left.size() > 1) { - throw new PathEngineException("Attempt to use as on more than one item ("+left.size()+")"); + if (!doNotEnforceAsSingletonRule && left.size() > 1) { + throw new PathEngineException("Attempt to use as on more than one item ("+left.size()+", '"+expr.toString()+"')"); } for (Base nextLeft : left) { - if (tn.equals(nextLeft.fhirType())) { + if (compareTypeNames(tn, nextLeft.fhirType())) { result.add(nextLeft); } } @@ -1801,9 +1827,19 @@ public class FHIRPathEngine { return result; } + private boolean compareTypeNames(String left, String right) { + if (doNotEnforceAsCaseSensitive) { + return left.equalsIgnoreCase(right); + } else { + return left.equals(right); + } + } private boolean isKnownType(String tn) { if (!tn.contains(".")) { + if (Utilities.existsInList(tn, "String", "Boolean", "Integer", "Decimal", "Quantity", "DateTime", "Time", "SimpleTypeInfo", "ClassInfo")) { + return true; + } try { return worker.fetchTypeDefinition(tn) != null; } catch (Exception e) { @@ -4584,7 +4620,7 @@ public class FHIRPathEngine { if (!isKnownType(tn)) { throw new PathEngineException("The type "+tn+" is not valid"); } - if (focus.size() > 1) { + if (!doNotEnforceAsSingletonRule && focus.size() > 1) { throw new PathEngineException("Attempt to use as() on more than one item ("+focus.size()+")"); } @@ -4603,7 +4639,7 @@ public class FHIRPathEngine { } else { StructureDefinition sd = worker.fetchTypeDefinition(b.fhirType()); while (sd != null) { - if (tnp.equals(sd.getType())) { + if (compareTypeNames(tnp, sd.getType())) { result.add(b); break; } diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java index 626a34e33..b0b065f60 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java @@ -201,6 +201,9 @@ public class FHIRPathTests { } } + fp.setDoNotEnforceAsCaseSensitive(false); + fp.setDoNotEnforceAsSingletonRule(false); + if (node != null) { try { outcome = fp.evaluate(res, node); diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java index c1793e5ae..1cf3d8a46 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java @@ -62,6 +62,7 @@ import org.hl7.fhir.utilities.MergedList; import org.hl7.fhir.utilities.MergedList.MergeNode; import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.xhtml.NodeType; @@ -257,6 +258,8 @@ public class FHIRPathEngine { private boolean allowPolymorphicNames; private boolean doImplicitStringConversion; private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host + private boolean doNotEnforceAsSingletonRule; + private boolean doNotEnforceAsCaseSensitive; // if the fhir path expressions are allowed to use constants beyond those defined in the specification // the application can implement them by providing a constant resolver @@ -366,8 +369,15 @@ public class FHIRPathEngine { primitiveTypes.add(sd.getName()); } } + initFlags(); } + private void initFlags() { + if (!VersionUtilities.isR5VerOrLater(worker.getVersion())) { + doNotEnforceAsCaseSensitive = true; + doNotEnforceAsSingletonRule = true; + } + } // --- 3 methods to override in children ------------------------------------------------------- // if you don't override, it falls through to the using the base reference implementation @@ -450,6 +460,22 @@ public class FHIRPathEngine { this.doImplicitStringConversion = doImplicitStringConversion; } + public boolean isDoNotEnforceAsSingletonRule() { + return doNotEnforceAsSingletonRule; + } + + public void setDoNotEnforceAsSingletonRule(boolean doNotEnforceAsSingletonRule) { + this.doNotEnforceAsSingletonRule = doNotEnforceAsSingletonRule; + } + + public boolean isDoNotEnforceAsCaseSensitive() { + return doNotEnforceAsCaseSensitive; + } + + public void setDoNotEnforceAsCaseSensitive(boolean doNotEnforceAsCaseSensitive) { + this.doNotEnforceAsCaseSensitive = doNotEnforceAsCaseSensitive; + } + // --- public API ------------------------------------------------------- /** * Parse a path for later use using execute @@ -1785,11 +1811,11 @@ public class FHIRPathEngine { if (!isKnownType(tn)) { throw new PathEngineException("The type "+tn+" is not valid"); } - if (left.size() > 1) { - throw new PathEngineException("Attempt to use as on more than one item ("+left.size()+")"); + if (!doNotEnforceAsSingletonRule && left.size() > 1) { + throw new PathEngineException("Attempt to use as on more than one item ("+left.size()+", '"+expr.toString()+"')"); } for (Base nextLeft : left) { - if (tn.equals(nextLeft.fhirType())) { + if (compareTypeNames(tn, nextLeft.fhirType())) { result.add(nextLeft); } } @@ -1797,9 +1823,19 @@ public class FHIRPathEngine { return result; } + private boolean compareTypeNames(String left, String right) { + if (doNotEnforceAsCaseSensitive) { + return left.equalsIgnoreCase(right); + } else { + return left.equals(right); + } + } private boolean isKnownType(String tn) { if (!tn.contains(".")) { + if (Utilities.existsInList(tn, "String", "Boolean", "Integer", "Decimal", "Quantity", "DateTime", "Time", "SimpleTypeInfo", "ClassInfo")) { + return true; + } try { return worker.fetchTypeDefinition(tn) != null; } catch (Exception e) { @@ -4580,7 +4616,7 @@ public class FHIRPathEngine { if (!isKnownType(tn)) { throw new PathEngineException("The type "+tn+" is not valid"); } - if (focus.size() > 1) { + if (!doNotEnforceAsSingletonRule && focus.size() > 1) { throw new PathEngineException("Attempt to use as() on more than one item ("+focus.size()+")"); } @@ -4599,7 +4635,7 @@ public class FHIRPathEngine { } else { StructureDefinition sd = worker.fetchTypeDefinition(b.fhirType()); while (sd != null) { - if (tnp.equals(sd.getType())) { + if (compareTypeNames(tnp, sd.getType())) { result.add(b); break; } diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java index 15d25b098..df02ac436 100644 --- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java @@ -201,6 +201,9 @@ public class FHIRPathTests { } } + fp.setDoNotEnforceAsCaseSensitive(false); + fp.setDoNotEnforceAsSingletonRule(false); + if (node != null) { try { outcome = fp.evaluate(res, node); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 25a19cc83..554dc8608 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -3864,5 +3864,9 @@ public class ProfileUtilities extends TranslatingUtilities { return null; } + public static boolean isExtensionDefinition(StructureDefinition sd) { + return sd.getDerivation() == TypeDerivationRule.CONSTRAINT && sd.getType().equals("Extension"); + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java index fe714306e..822efd798 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java @@ -136,12 +136,24 @@ public class ContextUtilities implements ProfileKnowledgeProvider { * @return a list of the resource and type names defined for this version */ public List getTypeNames() { - List result = new ArrayList(); + Set result = new HashSet(); + for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) { + if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) + result.add(sd.getName()); + } + return Utilities.sorted(result); + } + + + /** + * @return a set of the resource and type names defined for this version + */ + public Set getTypeNameSet() { + Set result = new HashSet(); for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) { if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) result.add(sd.getName()); } - Collections.sort(result); return result; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 8c71c8afa..f8f9f1d2a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -64,6 +64,7 @@ import org.hl7.fhir.utilities.MergedList; import org.hl7.fhir.utilities.MergedList.MergeNode; import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.xhtml.NodeType; @@ -264,6 +265,7 @@ public class FHIRPathEngine { private boolean doImplicitStringConversion; private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host private boolean doNotEnforceAsSingletonRule; + private boolean doNotEnforceAsCaseSensitive; // if the fhir path expressions are allowed to use constants beyond those defined in the specification // the application can implement them by providing a constant resolver @@ -373,8 +375,15 @@ public class FHIRPathEngine { primitiveTypes.add(sd.getName()); } } + initFlags(); } + private void initFlags() { + if (!VersionUtilities.isR5VerOrLater(worker.getVersion())) { + doNotEnforceAsCaseSensitive = true; + doNotEnforceAsSingletonRule = true; + } + } // --- 3 methods to override in children ------------------------------------------------------- // if you don't override, it falls through to the using the base reference implementation @@ -465,6 +474,14 @@ public class FHIRPathEngine { this.doNotEnforceAsSingletonRule = doNotEnforceAsSingletonRule; } + public boolean isDoNotEnforceAsCaseSensitive() { + return doNotEnforceAsCaseSensitive; + } + + public void setDoNotEnforceAsCaseSensitive(boolean doNotEnforceAsCaseSensitive) { + this.doNotEnforceAsCaseSensitive = doNotEnforceAsCaseSensitive; + } + // --- public API ------------------------------------------------------- /** * Parse a path for later use using execute @@ -1804,7 +1821,7 @@ public class FHIRPathEngine { throw new PathEngineException("Attempt to use as on more than one item ("+left.size()+", '"+expr.toString()+"')"); } for (Base nextLeft : left) { - if (tn.equals(nextLeft.fhirType())) { + if (compareTypeNames(tn, nextLeft.fhirType())) { result.add(nextLeft); } } @@ -1812,6 +1829,13 @@ public class FHIRPathEngine { return result; } + private boolean compareTypeNames(String left, String right) { + if (doNotEnforceAsCaseSensitive) { + return left.equalsIgnoreCase(right); + } else { + return left.equals(right); + } + } private boolean isKnownType(String tn) { if (!tn.contains(".")) { @@ -4617,7 +4641,7 @@ public class FHIRPathEngine { } else { StructureDefinition sd = worker.fetchTypeDefinition(b.fhirType()); while (sd != null) { - if (tnp.equals(sd.getType())) { + if (compareTypeNames(tnp, sd.getType())) { result.add(b); break; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java index 8447ee84a..e1c107d92 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java @@ -250,7 +250,7 @@ public class ToolingExtensions { Extension ex = new Extension(); // todo: write this up and get it published with the pack (and handle the redirect?) ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE); - CodeType c = new CodeType(); + StringType c = new StringType(); c.setValue(source.toString()); ex.setValue(c); return ex; diff --git a/pom.xml b/pom.xml index 367878697..44b5e8e6e 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 6.2.1 - 1.2.10 + 1.2.11-SNAPSHOT 5.7.1 1.8.2 3.0.0-M5