diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index eef7cf3d0..c7ea92444 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -53,6 +53,7 @@ import org.hl7.fhir.exceptions.NoTerminologyServiceException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; +import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory; import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; import org.hl7.fhir.r5.model.BooleanType; @@ -159,7 +160,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } private Object lock = new Object(); // used as a lock for the data that follows - protected String version; + protected String version; // although the internal resources are all R5, the version of FHIR they describe may not be private String cacheId; private boolean isTxCaching; private Set cached = new HashSet<>(); @@ -266,10 +267,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } - public void cachePackage(PackageVersion packageDetails, List dependencies) { - // nothing yet - } - + public void cacheResource(Resource r) throws FHIRException { cacheResourceFromPackage(r, null); } @@ -1317,6 +1315,65 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } + public PackageVersion getPackageForUrl(String uri) { + if (uri == null) { + return null; + } + uri = ProfileUtilities.sdNs(uri, getOverrideVersionNs()); + + synchronized (lock) { + + String version = null; + if (uri.contains("|")) { + version = uri.substring(uri.lastIndexOf("|")+1); + uri = uri.substring(0, uri.lastIndexOf("|")); + } + if (uri.contains("#")) { + uri = uri.substring(0, uri.indexOf("#")); + } + if (structures.has(uri)) { + return structures.getPackageInfo(uri, version); + } + if (guides.has(uri)) { + return guides.getPackageInfo(uri, version); + } + if (capstmts.has(uri)) { + return capstmts.getPackageInfo(uri, version); + } + if (measures.has(uri)) { + return measures.getPackageInfo(uri, version); + } + if (libraries.has(uri)) { + return libraries.getPackageInfo(uri, version); + } + if (valueSets.has(uri)) { + return valueSets.getPackageInfo(uri, version); + } + if (codeSystems.has(uri)) { + return codeSystems.getPackageInfo(uri, version); + } + if (operations.has(uri)) { + return operations.getPackageInfo(uri, version); + } + if (searchParameters.has(uri)) { + return searchParameters.getPackageInfo(uri, version); + } + if (plans.has(uri)) { + return plans.getPackageInfo(uri, version); + } + if (maps.has(uri)) { + return maps.getPackageInfo(uri, version); + } + if (transforms.has(uri)) { + return transforms.getPackageInfo(uri, version); + } + if (questionnaires.has(uri)) { + return questionnaires.getPackageInfo(uri, version); + } + return null; + } + } + @SuppressWarnings("unchecked") public T fetchResourceWithException(String cls, String uri, CanonicalResource source) throws FHIRException { if (uri == null) { @@ -1840,6 +1897,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } public String getLinkForUrl(String corePath, String url) { + if (url == null) { + return null; + } + if (codeSystems.has(url)) { return codeSystems.get(url).getUserString("path"); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java index efa9d4619..73b60a05e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java @@ -292,6 +292,20 @@ public class CanonicalResourceManager { return map.containsKey(url) ? map.get(url).getResource() : null; } + public PackageVersion getPackageInfo(String system, String version) { + if (version == null) { + return map.containsKey(system) ? map.get(system).getPackageInfo() : null; + } else { + if (map.containsKey(system+"|"+version)) + return map.get(system+"|"+version).getPackageInfo(); + String mm = VersionUtilities.getMajMin(version); + if (mm != null && map.containsKey(system+"|"+mm)) + return map.get(system+"|"+mm).getPackageInfo(); + else + return null; + } + } + public boolean has(String url) { return map.containsKey(url); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 5155a60a0..fd3b1c84e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -170,6 +170,27 @@ public interface IWorkerContext { } } + public class PackageDetails extends PackageVersion { + private String name; + private String canonical; + private String web; + public PackageDetails(String id, String version, String name, String canonical, String web) { + super(id, version); + this.name = name; + this.canonical = canonical; + this.web = web; + } + public String getName() { + return name; + } + public String getCanonical() { + return canonical; + } + public String getWeb() { + return web; + } + + } public interface ICanonicalResourceLocator { void findResource(Object caller, String url); // if it can be found, put it in the context } @@ -387,7 +408,7 @@ public interface IWorkerContext { * * @param packageInfo */ - public void cachePackage(PackageVersion packageDetails, List dependencies); + public void cachePackage(PackageDetails packageDetails, List dependencies); // -- profile services --------------------------------------------------------- @@ -779,10 +800,14 @@ public interface IWorkerContext { */ int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FileNotFoundException, IOException, FHIRException; - public boolean hasPackage(String id, String ver); + public boolean hasPackage(String id, String ver); + public boolean hasPackage(PackageVersion pack); + public PackageDetails getPackage(PackageVersion pack); public int getClientRetryCount(); public IWorkerContext setClientRetryCount(int value); public TimeTracker clock(); + + public PackageVersion getPackageForUrl(String url); } \ No newline at end of file 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 0eaabce29..e3796e404 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 @@ -54,6 +54,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; +import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory; import org.hl7.fhir.r5.context.SimpleWorkerContext.PackageResourceLoader; import org.hl7.fhir.r5.formats.IParser; @@ -810,15 +811,6 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon this.progress = progress; } - @Override - public boolean hasPackage(String id, String ver) { - return loadedPackages.contains(id+"#"+ver); - } - - public boolean hasPackage(String idAndver) { - return loadedPackages.contains(idAndver); - } - public void setClock(TimeTracker tt) { clock = tt; } @@ -837,5 +829,34 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon } return xverManager; } + + public void cachePackage(PackageVersion packageDetails, List dependencies) { + // nothing yet + } + + @Override + public boolean hasPackage(String id, String ver) { + return loadedPackages.contains(id+"#"+ver); + } + + public boolean hasPackage(String idAndver) { + return loadedPackages.contains(idAndver); + } + + @Override + public void cachePackage(PackageDetails packageDetails, List dependencies) { + // TODO Auto-generated method stub + } + + @Override + public boolean hasPackage(PackageVersion pack) { + return false; + } + + @Override + public PackageDetails getPackage(PackageVersion pack) { + return null; + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java index 73406ba26..727df9029 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java @@ -55,6 +55,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.utilities.ElementDecoration; import org.hl7.fhir.utilities.ElementDecoration.DecorationType; +import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.xhtml.XhtmlNode; @@ -102,6 +103,8 @@ public class Element extends Base { private String explicitType; // for xsi:type attribute private Element parentForValidator; private boolean hasParentForValidator; + private String path; + private List messages; public Element(String name) { super(); @@ -706,6 +709,13 @@ public class Element extends Base { return property.isList(); } + public boolean isBaseList() { + if (elementProperty != null) + return elementProperty.isBaseList(); + else + return property.isBaseList(); + } + @Override public String[] getTypesForProperty(int hash, String name) throws FHIRException { Property p = property.getChildSimpleName(this.name, name); @@ -938,6 +948,29 @@ public class Element extends Base { property = null; elementProperty = null; xhtml = null; + path = null; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; } + public void addMessage(ValidationMessage vm) { + if (messages == null) { + messages = new ArrayList<>(); + } + messages.add(vm); + } + + public boolean hasMessages() { + return messages != null && !messages.isEmpty(); + } + + public List getMessages() { + return messages; + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java index e617c6f9b..f2a3418e0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java @@ -3,19 +3,19 @@ package org.hl7.fhir.r5.elementmodel; /* 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 + + * 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, + * 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 + * 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. @@ -26,7 +26,7 @@ package org.hl7.fhir.r5.elementmodel; 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. - + */ @@ -72,32 +72,33 @@ import com.google.gson.JsonPrimitive; public class JsonParser extends ParserBase { - private JsonCreator json; - private Map map; - private boolean allowComments; + private JsonCreator json; + private Map map; + private boolean allowComments; private ProfileUtilities profileUtilities; public JsonParser(IWorkerContext context, ProfileUtilities utilities) { super(context); - + this.profileUtilities = utilities; } - + public JsonParser(IWorkerContext context) { super(context); - + this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context)); } - public Element parse(String source, String type) throws Exception { - JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source); + public Element parse(String source, String type) throws Exception { + JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source); String path = "/"+type; StructureDefinition sd = getDefinition(-1, -1, type); if (sd == null) return null; Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)); + result.setPath(type); checkObject(obj, path); result.setType(type); parseChildren(path, obj, result, true); @@ -106,138 +107,140 @@ public class JsonParser extends ParserBase { } - @Override - public Element parse(InputStream stream) throws IOException, FHIRException { - // if we're parsing at this point, then we're going to use the custom parser - map = new IdentityHashMap(); - String source = TextFile.streamToString(stream); - if (policy == ValidationPolicy.EVERYTHING) { - JsonObject obj = null; + @Override + public Element parse(InputStream stream) throws IOException, FHIRException { + // if we're parsing at this point, then we're going to use the custom parser + map = new IdentityHashMap(); + String source = TextFile.streamToString(stream); + if (policy == ValidationPolicy.EVERYTHING) { + JsonObject obj = null; try { - obj = JsonTrackingParser.parse(source, map, false, allowComments); + obj = JsonTrackingParser.parse(source, map, false, allowComments); } catch (Exception e) { - logError(-1, -1,context.formatMessage(I18nConstants.DOCUMENT), IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_, e.getMessage()), IssueSeverity.FATAL); - return null; + logError(-1, -1,context.formatMessage(I18nConstants.DOCUMENT), IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_, e.getMessage()), IssueSeverity.FATAL); + return null; } - assert (map.containsKey(obj)); - return parse(obj); - } else { - JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source); -// assert (map.containsKey(obj)); - return parse(obj); - } - } - - public Element parse(JsonObject object, Map map) throws FHIRException { - this.map = map; - return parse(object); - } - - public Element parse(JsonObject object) throws FHIRException { - JsonElement rt = object.get("resourceType"); - if (rt == null) { - logError(line(object), col(object), "$", IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); - return null; - } else { - String name = rt.getAsString(); - String path = name; - - StructureDefinition sd = getDefinition(line(object), col(object), name); - if (sd == null) - return null; - - Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)); - checkObject(object, path); - result.markLocation(line(object), col(object)); - result.setType(name); - parseChildren(path, object, result, true); - result.numberChildren(); - return result; - } - } - - private void checkObject(JsonObject object, String path) throws FHIRFormatError { - if (policy == ValidationPolicy.EVERYTHING) { - boolean found = false; - for (Entry e : object.entrySet()) { - // if (!e.getKey().equals("fhir_comments")) { - found = true; - break; - // } - } - if (!found) - logError(line(object), col(object), path, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR); - } - } - - private void parseChildren(String path, JsonObject object, Element element, boolean hasResourceType) throws FHIRException { - reapComments(object, element); - List properties = element.getProperty().getChildProperties(element.getName(), null); - Set processed = new HashSet(); - if (hasResourceType) - processed.add("resourceType"); - - // note that we do not trouble ourselves to maintain the wire format order here - we don't even know what it was anyway - // first pass: process the properties - for (Property property : properties) { - parseChildItem(path, object, element, processed, property); - } - - // second pass: check for things not processed - if (policy != ValidationPolicy.NONE) { - for (Entry e : object.entrySet()) { - if (!processed.contains(e.getKey())) { - logError(line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getKey()), IssueSeverity.ERROR); - } - } - } - } - - public void parseChildItem(String path, JsonObject object, Element context, Set processed, Property property) { - if (property.isChoice() || property.getDefinition().getPath().endsWith("data[x]")) { - for (TypeRefComponent type : property.getDefinition().getType()) { - String eName = property.getName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getWorkingCode()); - if (!isPrimitive(type.getWorkingCode()) && object.has(eName)) { - parseChildComplex(path, object, context, processed, property, eName); - break; - } else if (isPrimitive(type.getWorkingCode()) && (object.has(eName) || object.has("_"+eName))) { - parseChildPrimitive(object, context, processed, property, path, eName); - break; - } - } - } else if (property.isPrimitive(property.getType(null))) { - parseChildPrimitive(object, context, processed, property, path, property.getName()); - } else if (object.has(property.getName())) { - parseChildComplex(path, object, context, processed, property, property.getName()); + assert (map.containsKey(obj)); + return parse(obj); + } else { + JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source); + // assert (map.containsKey(obj)); + return parse(obj); } } - private void parseChildComplex(String path, JsonObject object, Element element, Set processed, Property property, String name) throws FHIRException { - processed.add(name); - String npath = path+"."+property.getName(); - JsonElement e = object.get(name); - if (property.isList() && (e instanceof JsonArray)) { - JsonArray arr = (JsonArray) e; - if (arr.size() == 0) { - logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ARRAY_CANNOT_BE_EMPTY), IssueSeverity.ERROR); - } - int c = 0; - for (JsonElement am : arr) { - parseChildComplexInstance(npath+"["+c+"]", object, element, property, name, am); - c++; - } - } else { - if (property.isList()) { - logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describeType(e), name, path), IssueSeverity.ERROR); - } - parseChildComplexInstance(npath, object, element, property, name, e); - } - } + public Element parse(JsonObject object, Map map) throws FHIRException { + this.map = map; + return parse(object); + } - private String describeType(JsonElement e) { - if (e.isJsonArray()) - return "an Array"; - if (e.isJsonObject()) + public Element parse(JsonObject object) throws FHIRException { + JsonElement rt = object.get("resourceType"); + if (rt == null) { + logError(line(object), col(object), "$", IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); + return null; + } else { + String name = rt.getAsString(); + String path = name; + + StructureDefinition sd = getDefinition(line(object), col(object), name); + if (sd == null) + return null; + + Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)); + checkObject(object, path); + result.markLocation(line(object), col(object)); + result.setType(name); + result.setPath(result.fhirType()); + parseChildren(path, object, result, true); + result.numberChildren(); + return result; + } + } + + private void checkObject(JsonObject object, String path) throws FHIRFormatError { + if (policy == ValidationPolicy.EVERYTHING) { + boolean found = false; + for (Entry e : object.entrySet()) { + // if (!e.getKey().equals("fhir_comments")) { + found = true; + break; + // } + } + if (!found) + logError(line(object), col(object), path, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR); + } + } + + private void parseChildren(String path, JsonObject object, Element element, boolean hasResourceType) throws FHIRException { + reapComments(object, element); + List properties = element.getProperty().getChildProperties(element.getName(), null); + Set processed = new HashSet(); + if (hasResourceType) + processed.add("resourceType"); + + // note that we do not trouble ourselves to maintain the wire format order here - we don't even know what it was anyway + // first pass: process the properties + for (Property property : properties) { + parseChildItem(path, object, element, processed, property); + } + + // second pass: check for things not processed + if (policy != ValidationPolicy.NONE) { + for (Entry e : object.entrySet()) { + if (!processed.contains(e.getKey())) { + logError(line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getKey()), IssueSeverity.ERROR); + } + } + } + } + + public void parseChildItem(String path, JsonObject object, Element context, Set processed, Property property) { + if (property.isChoice() || property.getDefinition().getPath().endsWith("data[x]")) { + for (TypeRefComponent type : property.getDefinition().getType()) { + String eName = property.getName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getWorkingCode()); + if (!isPrimitive(type.getWorkingCode()) && object.has(eName)) { + parseChildComplex(path, object, context, processed, property, eName); + break; + } else if (isPrimitive(type.getWorkingCode()) && (object.has(eName) || object.has("_"+eName))) { + parseChildPrimitive(object, context, processed, property, path, eName); + break; + } + } + } else if (property.isPrimitive(property.getType(null))) { + parseChildPrimitive(object, context, processed, property, path, property.getName()); + } else if (object.has(property.getName())) { + parseChildComplex(path, object, context, processed, property, property.getName()); + } + } + + private void parseChildComplex(String path, JsonObject object, Element element, Set processed, Property property, String name) throws FHIRException { + processed.add(name); + String npath = path+"."+property.getName(); + String fpath = element.getPath()+"."+property.getName(); + JsonElement e = object.get(name); + if (property.isList() && (e instanceof JsonArray)) { + JsonArray arr = (JsonArray) e; + if (arr.size() == 0) { + logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ARRAY_CANNOT_BE_EMPTY), IssueSeverity.ERROR); + } + int c = 0; + for (JsonElement am : arr) { + parseChildComplexInstance(npath+"["+c+"]", fpath+"["+c+"]", object, element, property, name, am); + c++; + } + } else { + if (property.isList()) { + logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describeType(e), name, path), IssueSeverity.ERROR); + } + parseChildComplexInstance(npath, fpath, object, element, property, name, e); + } + } + + private String describeType(JsonElement e) { + if (e.isJsonArray()) + return "an Array"; + if (e.isJsonObject()) return "an Object"; if (e.isJsonPrimitive()) return "a primitive property"; @@ -246,24 +249,25 @@ public class JsonParser extends ParserBase { return null; } - private void parseChildComplexInstance(String npath, JsonObject object, Element element, Property property, String name, JsonElement e) throws FHIRException { - if (e instanceof JsonObject) { - JsonObject child = (JsonObject) e; - Element n = new Element(name, property).markLocation(line(child), col(child)); - checkObject(child, npath); - element.getChildren().add(n); - if (property.isResource()) - parseResource(npath, child, n, property); - else - parseChildren(npath, child, n, false); - } else - logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE__NOT_, (property.isList() ? "an Array" : "an Object"), describe(e), name, npath), IssueSeverity.ERROR); - } + private void parseChildComplexInstance(String npath, String fpath, JsonObject object, Element element, Property property, String name, JsonElement e) throws FHIRException { + if (e instanceof JsonObject) { + JsonObject child = (JsonObject) e; + Element n = new Element(name, property).markLocation(line(child), col(child)); + n.setPath(fpath); + checkObject(child, npath); + element.getChildren().add(n); + if (property.isResource()) + parseResource(npath, child, n, property); + else + parseChildren(npath, child, n, false); + } else + logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE__NOT_, (property.isList() ? "an Array" : "an Object"), describe(e), name, npath), IssueSeverity.ERROR); + } - private String describe(JsonElement e) { - if (e instanceof JsonArray) { - return "an array"; - } + private String describe(JsonElement e) { + if (e instanceof JsonArray) { + return "an array"; + } if (e instanceof JsonObject) { return "an object"; } @@ -274,20 +278,21 @@ public class JsonParser extends ParserBase { } private void parseChildPrimitive(JsonObject object, Element element, Set processed, Property property, String path, String name) throws FHIRException { - String npath = path+"."+property.getName(); - processed.add(name); - processed.add("_"+name); - JsonElement main = object.has(name) ? object.get(name) : null; - JsonElement fork = object.has("_"+name) ? object.get("_"+name) : null; - if (main != null || fork != null) { - if (property.isList()) { - boolean ok = true; - if (!(main == null || main instanceof JsonArray)) { - logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main), name, path), IssueSeverity.ERROR); - ok = false; - } + String npath = path+"."+property.getName(); + String fpath = element.getPath()+"."+property.getName(); + processed.add(name); + processed.add("_"+name); + JsonElement main = object.has(name) ? object.get(name) : null; + JsonElement fork = object.has("_"+name) ? object.get("_"+name) : null; + if (main != null || fork != null) { + if (property.isList()) { + boolean ok = true; + if (!(main == null || main instanceof JsonArray)) { + logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main), name, path), IssueSeverity.ERROR); + ok = false; + } if (!(fork == null || fork instanceof JsonArray)) { - logError(line(fork), col(fork), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_BASE_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main), name, path), IssueSeverity.ERROR); + logError(line(fork), col(fork), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_BASE_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main), name, path), IssueSeverity.ERROR); ok = false; } if (ok) { @@ -296,158 +301,158 @@ public class JsonParser extends ParserBase { for (int i = 0; i < Math.max(arrC(arr1), arrC(arr2)); i++) { JsonElement m = arrI(arr1, i); JsonElement f = arrI(arr2, i); - parseChildPrimitiveInstance(element, property, name, npath, m, f); + parseChildPrimitiveInstance(element, property, name, npath, fpath, m, f); } } - } else { - parseChildPrimitiveInstance(element, property, name, npath, main, fork); - } - } - } + } else { + parseChildPrimitiveInstance(element, property, name, npath, fpath, main, fork); + } + } + } - private JsonElement arrI(JsonArray arr, int i) { - return arr == null || i >= arr.size() || arr.get(i) instanceof JsonNull ? null : arr.get(i); - } + private JsonElement arrI(JsonArray arr, int i) { + return arr == null || i >= arr.size() || arr.get(i) instanceof JsonNull ? null : arr.get(i); + } - private int arrC(JsonArray arr) { - return arr == null ? 0 : arr.size(); - } + private int arrC(JsonArray arr) { + return arr == null ? 0 : arr.size(); + } - private void parseChildPrimitiveInstance(Element element, Property property, String name, String npath, - JsonElement main, JsonElement fork) throws FHIRException { - if (main != null && !(main instanceof JsonPrimitive)) - logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage( + private void parseChildPrimitiveInstance(Element element, Property property, String name, String npath, String fpath, JsonElement main, JsonElement fork) throws FHIRException { + if (main != null && !(main instanceof JsonPrimitive)) + logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage( I18nConstants.THIS_PROPERTY_MUST_BE_AN_SIMPLE_VALUE_NOT_, describe(main), name, npath), IssueSeverity.ERROR); - else if (fork != null && !(fork instanceof JsonObject)) - logError(line(fork), col(fork), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(fork), name, npath), IssueSeverity.ERROR); - else { - Element n = new Element(name, property).markLocation(line(main != null ? main : fork), col(main != null ? main : fork)); - element.getChildren().add(n); - if (main != null) { - JsonPrimitive p = (JsonPrimitive) main; - if (p.isNumber() && p.getAsNumber() instanceof JsonTrackingParser.PresentedBigDecimal) { - String rawValue = ((JsonTrackingParser.PresentedBigDecimal) p.getAsNumber()).getPresentation(); - n.setValue(rawValue); - } else { - n.setValue(p.getAsString()); + else if (fork != null && !(fork instanceof JsonObject)) + logError(line(fork), col(fork), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(fork), name, npath), IssueSeverity.ERROR); + else { + Element n = new Element(name, property).markLocation(line(main != null ? main : fork), col(main != null ? main : fork)); + n.setPath(fpath); + element.getChildren().add(n); + if (main != null) { + JsonPrimitive p = (JsonPrimitive) main; + if (p.isNumber() && p.getAsNumber() instanceof JsonTrackingParser.PresentedBigDecimal) { + String rawValue = ((JsonTrackingParser.PresentedBigDecimal) p.getAsNumber()).getPresentation(); + n.setValue(rawValue); + } else { + n.setValue(p.getAsString()); + } + if (!n.getProperty().isChoice() && n.getType().equals("xhtml")) { + try { + n.setXhtml(new XhtmlParser().setValidatorMode(policy == ValidationPolicy.EVERYTHING).parse(n.getValue(), null).getDocumentElement()); + } catch (Exception e) { + logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_XHTML_, e.getMessage()), IssueSeverity.ERROR); } - if (!n.getProperty().isChoice() && n.getType().equals("xhtml")) { - try { - n.setXhtml(new XhtmlParser().setValidatorMode(policy == ValidationPolicy.EVERYTHING).parse(n.getValue(), null).getDocumentElement()); - } catch (Exception e) { - logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_XHTML_, e.getMessage()), IssueSeverity.ERROR); - } - } - if (policy == ValidationPolicy.EVERYTHING) { - // now we cross-check the primitive format against the stated type - if (Utilities.existsInList(n.getType(), "boolean")) { - if (!p.isBoolean()) - logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_BOOLEAN), IssueSeverity.ERROR); - } else if (Utilities.existsInList(n.getType(), "integer", "unsignedInt", "positiveInt", "decimal")) { - if (!p.isNumber()) - logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_NUMBER), IssueSeverity.ERROR); - } else if (!p.isString()) - logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_STRING), IssueSeverity.ERROR); - } - } - if (fork != null) { - JsonObject child = (JsonObject) fork; - checkObject(child, npath); - parseChildren(npath, child, n, false); - } - } - } + } + if (policy == ValidationPolicy.EVERYTHING) { + // now we cross-check the primitive format against the stated type + if (Utilities.existsInList(n.getType(), "boolean")) { + if (!p.isBoolean()) + logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_BOOLEAN), IssueSeverity.ERROR); + } else if (Utilities.existsInList(n.getType(), "integer", "unsignedInt", "positiveInt", "decimal")) { + if (!p.isNumber()) + logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_NUMBER), IssueSeverity.ERROR); + } else if (!p.isString()) + logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_STRING), IssueSeverity.ERROR); + } + } + if (fork != null) { + JsonObject child = (JsonObject) fork; + checkObject(child, npath); + parseChildren(npath, child, n, false); + } + } + } - private void parseResource(String npath, JsonObject res, Element parent, Property elementProperty) throws FHIRException { - JsonElement rt = res.get("resourceType"); - if (rt == null) { - logError(line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); - } else { - String name = rt.getAsString(); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs())); - if (sd == null) { + private void parseResource(String npath, JsonObject res, Element parent, Property elementProperty) throws FHIRException { + JsonElement rt = res.get("resourceType"); + if (rt == null) { + logError(line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); + } else { + String name = rt.getAsString(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs())); + if (sd == null) { logError(line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL); - } else { - parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty); - parent.setType(name); - parseChildren(npath, res, parent, true); - } - } - } + } else { + parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty); + parent.setType(name); + parseChildren(npath, res, parent, true); + } + } + } - private void reapComments(JsonObject object, Element context) { - if (object.has("fhir_comments")) { - JsonArray arr = object.getAsJsonArray("fhir_comments"); - for (JsonElement e : arr) { - context.getComments().add(e.getAsString()); - } - } - } + private void reapComments(JsonObject object, Element context) { + if (object.has("fhir_comments")) { + JsonArray arr = object.getAsJsonArray("fhir_comments"); + for (JsonElement e : arr) { + context.getComments().add(e.getAsString()); + } + } + } - private int line(JsonElement e) { - if (map == null|| !map.containsKey(e)) - return -1; - else - return map.get(e).getLine(); - } + private int line(JsonElement e) { + if (map == null|| !map.containsKey(e)) + return -1; + else + return map.get(e).getLine(); + } - private int col(JsonElement e) { - if (map == null|| !map.containsKey(e)) - return -1; - else - return map.get(e).getCol(); - } + private int col(JsonElement e) { + if (map == null|| !map.containsKey(e)) + return -1; + else + return map.get(e).getCol(); + } - protected void prop(String name, String value, String link) throws IOException { + protected void prop(String name, String value, String link) throws IOException { json.link(link); - if (name != null) - json.name(name); - json.value(value); - } + if (name != null) + json.name(name); + json.value(value); + } - protected void open(String name, String link) throws IOException { - json.link(link); - if (name != null) - json.name(name); - json.beginObject(); - } - - protected void close() throws IOException { - json.endObject(); - } - - protected void openArray(String name, String link) throws IOException { + protected void open(String name, String link) throws IOException { json.link(link); - if (name != null) - json.name(name); - json.beginArray(); - } + if (name != null) + json.name(name); + json.beginObject(); + } - protected void closeArray() throws IOException { - json.endArray(); - } + protected void close() throws IOException { + json.endObject(); + } + + protected void openArray(String name, String link) throws IOException { + json.link(link); + if (name != null) + json.name(name); + json.beginArray(); + } + + protected void closeArray() throws IOException { + json.endArray(); + } - @Override - public void compose(Element e, OutputStream stream, OutputStyle style, String identity) throws FHIRException, IOException { - OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); - if (style == OutputStyle.CANONICAL) - json = new JsonCreatorCanonical(osw); - else - json = new JsonCreatorGson(osw); - json.setIndent(style == OutputStyle.PRETTY ? " " : ""); - json.beginObject(); - prop("resourceType", e.getType(), null); - Set done = new HashSet(); - for (Element child : e.getChildren()) { - compose(e.getName(), e, done, child); - } - json.endObject(); - json.finish(); - osw.flush(); - } + @Override + public void compose(Element e, OutputStream stream, OutputStyle style, String identity) throws FHIRException, IOException { + OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); + if (style == OutputStyle.CANONICAL) + json = new JsonCreatorCanonical(osw); + else + json = new JsonCreatorGson(osw); + json.setIndent(style == OutputStyle.PRETTY ? " " : ""); + json.beginObject(); + prop("resourceType", e.getType(), null); + Set done = new HashSet(); + for (Element child : e.getChildren()) { + compose(e.getName(), e, done, child); + } + json.endObject(); + json.finish(); + osw.flush(); + } public void compose(Element e, JsonCreator json) throws Exception { this.json = json; @@ -462,104 +467,104 @@ public class JsonParser extends ParserBase { json.finish(); } - private void compose(String path, Element e, Set done, Element child) throws IOException { - boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList(); - if (!isList) {// for specials, ignore the cardinality of the stated type - compose(path, child); - } else if (!done.contains(child.getName())) { - done.add(child.getName()); - List list = e.getChildrenByName(child.getName()); - composeList(path, list); - } - } + private void compose(String path, Element e, Set done, Element child) throws IOException { + boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList(); + if (!isList) {// for specials, ignore the cardinality of the stated type + compose(path, child); + } else if (!done.contains(child.getName())) { + done.add(child.getName()); + List list = e.getChildrenByName(child.getName()); + composeList(path, list); + } + } - private void composeList(String path, List list) throws IOException { - // there will be at least one element - String name = list.get(0).getName(); - boolean complex = true; - if (list.get(0).isPrimitive()) { - boolean prim = false; - complex = false; - for (Element item : list) { - if (item.hasValue()) - prim = true; - if (item.hasChildren()) - complex = true; - } - if (prim) { - openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); - for (Element item : list) { - if (item.hasValue()) - primitiveValue(null, item); - else - json.nullValue(); - } - closeArray(); - } - name = "_"+name; - } - if (complex) { - openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); - for (Element item : list) { - if (item.hasChildren()) { - open(null,null); - if (item.getProperty().isResource()) { - prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType())); - } - Set done = new HashSet(); - for (Element child : item.getChildren()) { - compose(path+"."+name+"[]", item, done, child); - } - close(); - } else - json.nullValue(); - } - closeArray(); - } - } + private void composeList(String path, List list) throws IOException { + // there will be at least one element + String name = list.get(0).getName(); + boolean complex = true; + if (list.get(0).isPrimitive()) { + boolean prim = false; + complex = false; + for (Element item : list) { + if (item.hasValue()) + prim = true; + if (item.hasChildren()) + complex = true; + } + if (prim) { + openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); + for (Element item : list) { + if (item.hasValue()) + primitiveValue(null, item); + else + json.nullValue(); + } + closeArray(); + } + name = "_"+name; + } + if (complex) { + openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); + for (Element item : list) { + if (item.hasChildren()) { + open(null,null); + if (item.getProperty().isResource()) { + prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType())); + } + Set done = new HashSet(); + for (Element child : item.getChildren()) { + compose(path+"."+name+"[]", item, done, child); + } + close(); + } else + json.nullValue(); + } + closeArray(); + } + } - private void primitiveValue(String name, Element item) throws IOException { - if (name != null) { - if (linkResolver != null) - json.link(linkResolver.resolveProperty(item.getProperty())); - json.name(name); - } - String type = item.getType(); - if (Utilities.existsInList(type, "boolean")) - json.value(item.getValue().trim().equals("true") ? new Boolean(true) : new Boolean(false)); - else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) - json.value(new Integer(item.getValue())); - else if (Utilities.existsInList(type, "decimal")) - try { - json.value(new BigDecimal(item.getValue())); - } catch (Exception e) { - throw new NumberFormatException(context.formatMessage(I18nConstants.ERROR_WRITING_NUMBER__TO_JSON, item.getValue())); - } - else - json.value(item.getValue()); - } + private void primitiveValue(String name, Element item) throws IOException { + if (name != null) { + if (linkResolver != null) + json.link(linkResolver.resolveProperty(item.getProperty())); + json.name(name); + } + String type = item.getType(); + if (Utilities.existsInList(type, "boolean")) + json.value(item.getValue().trim().equals("true") ? new Boolean(true) : new Boolean(false)); + else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) + json.value(new Integer(item.getValue())); + else if (Utilities.existsInList(type, "decimal")) + try { + json.value(new BigDecimal(item.getValue())); + } catch (Exception e) { + throw new NumberFormatException(context.formatMessage(I18nConstants.ERROR_WRITING_NUMBER__TO_JSON, item.getValue())); + } + else + json.value(item.getValue()); + } - private void compose(String path, Element element) throws IOException { - String name = element.getName(); - if (element.isPrimitive() || isPrimitive(element.getType())) { - if (element.hasValue()) - primitiveValue(name, element); - name = "_"+name; - if (element.getType().equals("xhtml")) - json.anchor("end-xhtml"); - } - if (element.hasChildren()) { - open(name, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); - if (element.getProperty().isResource()) { - prop("resourceType", element.getType(), linkResolver == null ? null : linkResolver.resolveType(element.getType())); - } - Set done = new HashSet(); - for (Element child : element.getChildren()) { - compose(path+"."+element.getName(), element, done, child); - } - close(); - } - } + private void compose(String path, Element element) throws IOException { + String name = element.getName(); + if (element.isPrimitive() || isPrimitive(element.getType())) { + if (element.hasValue()) + primitiveValue(name, element); + name = "_"+name; + if (element.getType().equals("xhtml")) + json.anchor("end-xhtml"); + } + if (element.hasChildren()) { + open(name, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); + if (element.getProperty().isResource()) { + prop("resourceType", element.getType(), linkResolver == null ? null : linkResolver.resolveType(element.getType())); + } + Set done = new HashSet(); + for (Element child : element.getChildren()) { + compose(path+"."+element.getName(), element, done, child); + } + close(); + } + } public boolean isAllowComments() { return allowComments; @@ -570,5 +575,5 @@ public class JsonParser extends ParserBase { return this; } - + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java index 646fa5c82..84b8e2482 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java @@ -228,9 +228,13 @@ public class Property { return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE); } - public boolean isList() { - return !"1".equals(definition.getMax()); - } + public boolean isList() { + return !"1".equals(definition.getMax()); + } + + public boolean isBaseList() { + return !"1".equals(definition.getBase().getMax()); + } public String getScopedPropertyName() { return definition.getBase().getPath(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java index b50a97fc4..88f38a5f5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/XmlParser.java @@ -203,6 +203,7 @@ public class XmlParser extends ParserBase { return null; Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)); + result.setPath(element.getLocalName()); checkElement(element, path, result.getProperty()); result.markLocation(line(element), col(element)); result.setType(element.getLocalName()); @@ -262,6 +263,7 @@ public class XmlParser extends ParserBase { public Element parse(org.w3c.dom.Element base, String type) throws Exception { StructureDefinition sd = getDefinition(0, 0, FormatUtilities.FHIR_NS, type); Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)); + result.setPath(base.getLocalName()); String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName(); checkElement(base, path, result.getProperty()); result.setType(base.getLocalName()); @@ -283,13 +285,18 @@ public class XmlParser extends ParserBase { if (property != null) { if ("ED.data[x]".equals(property.getDefinition().getId()) || (property.getDefinition()!=null && property.getDefinition().getBase()!=null && "ED.data[x]".equals(property.getDefinition().getBase().getPath()))) { if ("B64".equals(node.getAttribute("representation"))) { - element.getChildren().add(new Element("dataBase64Binary", property, "base64Binary", text).markLocation(line, col)); + Element n = new Element("dataBase64Binary", property, "base64Binary", text).markLocation(line, col); + n.setPath(element.getPath()+"."+property.getName()); + element.getChildren().add(n); } else { - element.getChildren().add(new Element("dataString", property, "string", text).markLocation(line, col)); + Element n = new Element("dataString", property, "string", text).markLocation(line, col); + n.setPath(element.getPath()+"."+property.getName()); + element.getChildren().add(n); } } else { - element.getChildren().add( - new Element(property.getName(), property, property.getType(), text).markLocation(line, col)); + Element n = new Element(property.getName(), property, property.getType(), text).markLocation(line, col); + n.setPath(element.getPath()+"."+property.getName()); + element.getChildren().add(n); } } else { @@ -325,8 +332,11 @@ public class XmlParser extends ParserBase { av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av); if (property.getName().equals("value") && element.isPrimitive()) element.setValue(av); - else - element.getChildren().add(new Element(property.getName(), property, property.getType(), av).markLocation(line, col)); + else { + Element n = new Element(property.getName(), property, property.getType(), av).markLocation(line, col); + n.setPath(element.getPath()+"."+property.getName()); + element.getChildren().add(n); + } } else { boolean ok = false; if (FormatUtilities.FHIR_NS.equals(node.getNamespaceURI())) { @@ -342,21 +352,36 @@ public class XmlParser extends ParserBase { } } + String lastName = null; + int repeatCount = 0; Node child = node.getFirstChild(); while (child != null) { if (child.getNodeType() == Node.ELEMENT_NODE) { Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI()); if (property != null) { + if (property.getName().equals(lastName)) { + repeatCount++; + } else { + lastName = property.getName(); + repeatCount = 0; + } if (!property.isChoice() && "xhtml".equals(property.getType())) { XhtmlNode xhtml; if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT)) xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child); else xhtml = new XhtmlParser().setValidatorMode(true).parseHtmlNode((org.w3c.dom.Element) child); - element.getChildren().add(new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child), col(child))); + Element n = new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child), col(child)); + n.setPath(element.getPath()+"."+property.getName()); + element.getChildren().add(n); } else { String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName(); Element n = new Element(child.getLocalName(), property).markLocation(line(child), col(child)); + if (property.isList()) { + n.setPath(element.getPath()+"."+property.getName()+"["+repeatCount+"]"); + } else { + n.setPath(element.getPath()+"."+property.getName()); + } checkElement((org.w3c.dom.Element) child, npath, n.getProperty()); boolean ok = true; if (property.isChoice()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java index fc9efb397..ad80d7ae4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java @@ -132,7 +132,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { li.tx(opt.getValue().primitiveValue()); } else if (opt.getValue() instanceof Coding) { Coding c = (Coding) opt.getValue(); - String link = context.getWorker().getLinkForUrl(context.getSpecificationLink(), c.getSystem()); + String link = c.hasSystem() ? context.getWorker().getLinkForUrl(context.getSpecificationLink(), c.getSystem()) : null; if (link == null) { li.tx(c.getSystem()+"#"+c.getCode()); } else { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 20426d1ff..9fff78ee7 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -583,6 +583,7 @@ public class I18nConstants { public static final String VALIDATION_VAL_PROFILE_NOTSLICE = "Validation_VAL_Profile_NotSlice"; public static final String VALIDATION_VAL_PROFILE_NOTYPE = "Validation_VAL_Profile_NoType"; public static final String VALIDATION_VAL_PROFILE_OUTOFORDER = "Validation_VAL_Profile_OutOfOrder"; + public static final String VALIDATION_VAL_PROFILE_SIGNPOST_BASE = "VALIDATION_VAL_PROFILE_SIGNPOST_BASE"; public static final String VALIDATION_VAL_PROFILE_SIGNPOST = "VALIDATION_VAL_PROFILE_SIGNPOST"; public static final String VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL = "VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL"; public static final String VALIDATION_VAL_PROFILE_SIGNPOST_META = "VALIDATION_VAL_PROFILE_SIGNPOST_META"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java index cbd5662d7..e54ad176a 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java @@ -790,8 +790,9 @@ public class ValidationMessage implements Comparator, Compara return signpost; } - public void setSignpost(boolean signpost) { + public ValidationMessage setSignpost(boolean signpost) { this.signpost = signpost; + return this; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 7b35df80d..8a9d622a3 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -524,8 +524,9 @@ TYPE_CHECKS_PATTERN_CC_US = The pattern [system {0}, code {1}, display ''{2}'' a TYPE_CHECKS_FIXED_CC = The pattern [system {0}, code {1}, and display ''{2}''] defined in the profile {3} not found. Issues: {4} TYPE_CHECKS_FIXED_CC_US = The pattern [system {0}, code {1}, display ''{2}'' and userSelected {5}] defined in the profile {3} not found. Issues: {4} VALIDATION_VAL_GLOBAL_PROFILE_UNKNOWN = Global Profile reference ''{0}'' from IG {1} could not be resolved, so has not been checked +VALIDATION_VAL_PROFILE_SIGNPOST_BASE = Validate resource against profile VALIDATION_VAL_PROFILE_SIGNPOST = Validate resource against profile {0} -VALIDATION_VAL_PROFILE_SIGNPOST_META = Validate resource against profile {0} - listed in meta +VALIDATION_VAL_PROFILE_SIGNPOST_META = Validate resource against profile {0} (per meta) VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM = Validate resource against profile {0} - provided as bundle param VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL = Validate resource against profile {0} - a global profile in {1} ERROR_GENERATING_SNAPSHOT = Error generating Snapshot: {0} (this usually arises from a problem in the differential) @@ -618,7 +619,7 @@ SD_NESTED_MUST_SUPPORT_SNAPSHOT = The element {0} has types/profiles/targets tha Unable_to_connect_to_terminology_server = Unable to connect to terminology server. Error = {0} SD_ED_TYPE_PROFILE_UNKNOWN = Unable to resolve profile {0} SD_ED_TYPE_PROFILE_NOTYPE = Found profile {0}, but unable to determine the type it applies to -SD_ED_TYPE_PROFILE_WRONG = Extension {0} is for type {1}, but the {3} element has type {2} +SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but the {3} element has type {2} SD_ED_TYPE_PROFILE_WRONG_TARGET = Profile {0} is for type {1}, which is not a {4} (which is required because the {3} element has type {2}) SD_ED_TYPE_NO_TARGET_PROFILE = Type {0} does not allow for target Profiles TERMINOLOGY_TX_NOSVC_BOUND_REQ = Could not confirm that the codes provided are from the required value set {0} because there is no terminology service diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties b/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties index 7c6b27c7f..23b9be242 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties @@ -618,7 +618,7 @@ SD_NESTED_MUST_SUPPORT_SNAPSHOT = Het element {0} heeft typen/profielen/targets Unable_to_connect_to_terminology_server = Kan niet verbinden met terminologieserver. Fout = {0} SD_ED_TYPE_PROFILE_UNKNOWN = Kan profiel {0} niet vinden SD_ED_TYPE_PROFILE_NOTYPE = Profiel {0} gevonden, maar kan niet bepalen op welke type deze van toepassing is -SD_ED_TYPE_PROFILE_WRONG = Extensie {0} is voor type {1}, met het {3} element heeft type {2} +SD_ED_TYPE_PROFILE_WRONG = Profiel {0} is voor type {1}, met het {3} element heeft type {2} SD_ED_TYPE_PROFILE_WRONG_TARGET = Profiel {0} is voor type {1}, wat geen {4} is (welke wordt vereist om het {3} element type {2} heeft) SD_ED_TYPE_NO_TARGET_PROFILE = Type {0} laat geen target Profielen toe TERMINOLOGY_TX_NOSVC_BOUND_REQ = Kan niet bevestigen dat de gevonden codes bestaan in de verplichte waardelijst {0} omdat er geen terminologieservice is diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index da3947d4c..798a47d7c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -284,12 +284,9 @@ public class BaseValidator { return thePass; } - protected boolean signpost(List errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { - if (!thePass) { - String message = context.formatMessage(theMessage, theMessageArguments); - addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage).setSignpost(true); - } - return thePass; + protected ValidationMessage signpost(List errors, IssueType type, int line, int col, String path, String theMessage, Object... theMessageArguments) { + String message = context.formatMessage(theMessage, theMessageArguments); + return addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage).setSignpost(true); } protected boolean txHint(List errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { 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 bded58268..729963403 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 @@ -3270,8 +3270,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rr.setResource(nstack.getElement()); rr.setFocus(nstack.getElement()); rr.setExternal(false); - rr.setStack(nstack.push(nstack.getElement(), -1, nstack.getElement().getProperty().getDefinition(), nstack.getElement().getProperty().getDefinition())); - rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")"); + rr.setStack(nstack); +// rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")"); + System.out.println("-->"+nstack.getLiteralPath()); return rr; } if (nstack.getElement().getSpecial() == SpecialElement.CONTAINED) { @@ -3951,7 +3952,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) private void start(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { checkLang(resource, stack); - signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), !crumbTrails, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST, defn.getUrl()); + if (crumbTrails) { + element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST, defn.getUrl())); + } if (BUNDLE.equals(element.fhirType())) { resolveBundleReferences(element, new ArrayList()); @@ -3996,7 +3999,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (sd != null) { - signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), !crumbTrails, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl()); + if (crumbTrails) { + element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl())); + } stack.resetIds(); startInner(hostContext, errors, resource, element, sd, stack, false); } @@ -4011,7 +4016,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (rt.equals(gl.getType())) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, gl.getProfile()); if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), sd != null, I18nConstants.VALIDATION_VAL_GLOBAL_PROFILE_UNKNOWN, gl.getProfile())) { - signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), !crumbTrails, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL, sd.getUrl(), ig.getUrl()); + if (crumbTrails) { + element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL, sd.getUrl(), ig.getUrl())); + } stack.resetIds(); startInner(hostContext, errors, resource, element, sd, stack, false); } @@ -4517,13 +4524,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat type = null; } } - NodeStack localStack = stack.push(ei.getElement(), ei.count, checkDefn, type == null ? typeDefn : resolveType(type, checkDefn.getType())); + NodeStack localStack = stack.push(ei.getElement(), "*".equals(ei.getDefinition().getBase().getMax()) && ei.count == -1 ? 0 : ei.count, checkDefn, type == null ? typeDefn : resolveType(type, checkDefn.getType())); // if (debug) { // System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()); // } - String localStackLiterapPath = localStack.getLiteralPath(); + String localStackLiteralPath = localStack.getLiteralPath(); String eiPath = ei.getPath(); - assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath; + if (!eiPath.equals(localStackLiteralPath)) { + assert (eiPath.equals(localStackLiteralPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiteralPath; + } boolean thisIsCodeableConcept = false; String thisExtension = null; boolean checkDisplay = true; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java index f321ac948..78a06b761 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java @@ -112,7 +112,9 @@ public class BundleValidator extends BaseValidator{ } else { Element res = entry.getNamedChild(RESOURCE); NodeStack rstack = estack.push(res, -1, null, null); - signpost(errors, IssueType.INFORMATIONAL, res.line(), res.col(), stack.getLiteralPath(), !validator.isCrumbTrails(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM, defn.getUrl()); + if (validator.isCrumbTrails()) { + res.addMessage(signpost(errors, IssueType.INFORMATIONAL, res.line(), res.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM, defn.getUrl())); + } stack.resetIds(); validator.startInner(hostContext, errors, res, res, defn, rstack, false); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index fed818aca..e9aaedfcc 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -247,8 +247,7 @@ public class StructureDefinitionValidator extends BaseValidator { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { StructureDefinition t = determineBaseType(sd); if (t == null) { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); - throw new Error("What to do about this?"); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); } @@ -260,8 +259,7 @@ public class StructureDefinitionValidator extends BaseValidator { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { StructureDefinition t = determineBaseType(sd); if (t == null) { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); - throw new Error("What to do about this?"); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); } @@ -317,7 +315,7 @@ public class StructureDefinitionValidator extends BaseValidator { if (t == null) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else if (!VersionUtilities.isR5Ver(context.getVersion())) { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t.getType()) || "Resource".equals(t), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Canonical Resource"); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t.getType()) || "Resource".equals(t.getType()), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Canonical Resource"); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t.getType()), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Canonical Resource"); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ChildIterator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ChildIterator.java index 7d1520046..398f947f5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ChildIterator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ChildIterator.java @@ -70,9 +70,12 @@ public class ChildIterator { String nb = cursor == 0 ? "--" : parent.getChildren().get(cursor - 1).getName(); String na = cursor >= parent.getChildren().size() - 1 ? "--" : parent.getChildren().get(cursor + 1).getName(); if (name().equals(nb) || name().equals(na)) { - return lastCount; - } else - return -1; + return lastCount; + } else if (element().isBaseList()) { + return 0; + } else { + return -1; + } } public boolean next() { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java index abb85ac64..d1f7fe45d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/NodeStack.java @@ -34,7 +34,7 @@ public class NodeStack { this.context = context; ids = new HashMap<>(); this.element = element; - literalPath = element.getName(); + literalPath = element.getPath(); workingLang = validationLanguage; if (!element.getName().equals(element.fhirType())) { logicalPaths = new ArrayList<>();