Merge pull request #544 from hapifhir/gg-202106-validator-paths

Gg 202106 validator paths
This commit is contained in:
Grahame Grieve 2021-06-23 22:07:22 +10:00 committed by GitHub
commit e022acb569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 652 additions and 452 deletions

View File

@ -53,6 +53,7 @@ import org.hl7.fhir.exceptions.NoTerminologyServiceException;
import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; 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.IWorkerContext.ILoggingService.LogCategory;
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
import org.hl7.fhir.r5.model.BooleanType; 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 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 String cacheId;
private boolean isTxCaching; private boolean isTxCaching;
private Set<String> cached = new HashSet<>(); private Set<String> cached = new HashSet<>();
@ -266,10 +267,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
} }
} }
public void cachePackage(PackageVersion packageDetails, List<PackageVersion> dependencies) {
// nothing yet
}
public void cacheResource(Resource r) throws FHIRException { public void cacheResource(Resource r) throws FHIRException {
cacheResourceFromPackage(r, null); 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") @SuppressWarnings("unchecked")
public <T extends Resource> T fetchResourceWithException(String cls, String uri, CanonicalResource source) throws FHIRException { public <T extends Resource> T fetchResourceWithException(String cls, String uri, CanonicalResource source) throws FHIRException {
if (uri == null) { if (uri == null) {
@ -1840,6 +1897,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
} }
public String getLinkForUrl(String corePath, String url) { public String getLinkForUrl(String corePath, String url) {
if (url == null) {
return null;
}
if (codeSystems.has(url)) { if (codeSystems.has(url)) {
return codeSystems.get(url).getUserString("path"); return codeSystems.get(url).getUserString("path");
} }

View File

@ -292,6 +292,20 @@ public class CanonicalResourceManager<T extends CanonicalResource> {
return map.containsKey(url) ? map.get(url).getResource() : null; 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) { public boolean has(String url) {
return map.containsKey(url); return map.containsKey(url);
} }

View File

@ -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 { public interface ICanonicalResourceLocator {
void findResource(Object caller, String url); // if it can be found, put it in the context 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 * @param packageInfo
*/ */
public void cachePackage(PackageVersion packageDetails, List<PackageVersion> dependencies); public void cachePackage(PackageDetails packageDetails, List<PackageVersion> dependencies);
// -- profile services --------------------------------------------------------- // -- profile services ---------------------------------------------------------
@ -779,10 +800,14 @@ public interface IWorkerContext {
*/ */
int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FileNotFoundException, IOException, FHIRException; 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 int getClientRetryCount();
public IWorkerContext setClientRetryCount(int value); public IWorkerContext setClientRetryCount(int value);
public TimeTracker clock(); public TimeTracker clock();
public PackageVersion getPackageForUrl(String url);
} }

View File

@ -54,6 +54,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; 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.IWorkerContext.ILoggingService.LogCategory;
import org.hl7.fhir.r5.context.SimpleWorkerContext.PackageResourceLoader; import org.hl7.fhir.r5.context.SimpleWorkerContext.PackageResourceLoader;
import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.IParser;
@ -810,15 +811,6 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
this.progress = progress; 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) { public void setClock(TimeTracker tt) {
clock = tt; clock = tt;
} }
@ -837,5 +829,34 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
} }
return xverManager; return xverManager;
} }
public void cachePackage(PackageVersion packageDetails, List<PackageVersion> 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<PackageVersion> dependencies) {
// TODO Auto-generated method stub
}
@Override
public boolean hasPackage(PackageVersion pack) {
return false;
}
@Override
public PackageDetails getPackage(PackageVersion pack) {
return null;
}
} }

View File

@ -55,6 +55,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.ElementDecoration; import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.ElementDecoration.DecorationType; 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.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -102,6 +103,8 @@ public class Element extends Base {
private String explicitType; // for xsi:type attribute private String explicitType; // for xsi:type attribute
private Element parentForValidator; private Element parentForValidator;
private boolean hasParentForValidator; private boolean hasParentForValidator;
private String path;
private List<ValidationMessage> messages;
public Element(String name) { public Element(String name) {
super(); super();
@ -706,6 +709,13 @@ public class Element extends Base {
return property.isList(); return property.isList();
} }
public boolean isBaseList() {
if (elementProperty != null)
return elementProperty.isBaseList();
else
return property.isBaseList();
}
@Override @Override
public String[] getTypesForProperty(int hash, String name) throws FHIRException { public String[] getTypesForProperty(int hash, String name) throws FHIRException {
Property p = property.getChildSimpleName(this.name, name); Property p = property.getChildSimpleName(this.name, name);
@ -938,6 +948,29 @@ public class Element extends Base {
property = null; property = null;
elementProperty = null; elementProperty = null;
xhtml = 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<ValidationMessage> getMessages() {
return messages;
}
} }

View File

@ -3,19 +3,19 @@ package org.hl7.fhir.r5.elementmodel;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: 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. 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 this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. 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 endorse or promote products derived from this software without specific
prior written permission. prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 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 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 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) 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 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -72,32 +72,33 @@ import com.google.gson.JsonPrimitive;
public class JsonParser extends ParserBase { public class JsonParser extends ParserBase {
private JsonCreator json; private JsonCreator json;
private Map<JsonElement, LocationData> map; private Map<JsonElement, LocationData> map;
private boolean allowComments; private boolean allowComments;
private ProfileUtilities profileUtilities; private ProfileUtilities profileUtilities;
public JsonParser(IWorkerContext context, ProfileUtilities utilities) { public JsonParser(IWorkerContext context, ProfileUtilities utilities) {
super(context); super(context);
this.profileUtilities = utilities; this.profileUtilities = utilities;
} }
public JsonParser(IWorkerContext context) { public JsonParser(IWorkerContext context) {
super(context); super(context);
this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context)); this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context));
} }
public Element parse(String source, String type) throws Exception { public Element parse(String source, String type) throws Exception {
JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source); JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source);
String path = "/"+type; String path = "/"+type;
StructureDefinition sd = getDefinition(-1, -1, type); StructureDefinition sd = getDefinition(-1, -1, type);
if (sd == null) if (sd == null)
return null; return null;
Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)); Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities));
result.setPath(type);
checkObject(obj, path); checkObject(obj, path);
result.setType(type); result.setType(type);
parseChildren(path, obj, result, true); parseChildren(path, obj, result, true);
@ -106,138 +107,140 @@ public class JsonParser extends ParserBase {
} }
@Override @Override
public Element parse(InputStream stream) throws IOException, FHIRException { public Element parse(InputStream stream) throws IOException, FHIRException {
// if we're parsing at this point, then we're going to use the custom parser // if we're parsing at this point, then we're going to use the custom parser
map = new IdentityHashMap<JsonElement, LocationData>(); map = new IdentityHashMap<JsonElement, LocationData>();
String source = TextFile.streamToString(stream); String source = TextFile.streamToString(stream);
if (policy == ValidationPolicy.EVERYTHING) { if (policy == ValidationPolicy.EVERYTHING) {
JsonObject obj = null; JsonObject obj = null;
try { try {
obj = JsonTrackingParser.parse(source, map, false, allowComments); obj = JsonTrackingParser.parse(source, map, false, allowComments);
} catch (Exception e) { } catch (Exception e) {
logError(-1, -1,context.formatMessage(I18nConstants.DOCUMENT), IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_, e.getMessage()), IssueSeverity.FATAL); logError(-1, -1,context.formatMessage(I18nConstants.DOCUMENT), IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_, e.getMessage()), IssueSeverity.FATAL);
return null; return null;
} }
assert (map.containsKey(obj)); assert (map.containsKey(obj));
return parse(obj); return parse(obj);
} else { } else {
JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source); JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source);
// assert (map.containsKey(obj)); // assert (map.containsKey(obj));
return parse(obj); return parse(obj);
}
}
public Element parse(JsonObject object, Map<JsonElement, LocationData> 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<String, JsonElement> 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<Property> properties = element.getProperty().getChildProperties(element.getName(), null);
Set<String> processed = new HashSet<String>();
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<String, JsonElement> 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<String> 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<String> processed, Property property, String name) throws FHIRException { public Element parse(JsonObject object, Map<JsonElement, LocationData> map) throws FHIRException {
processed.add(name); this.map = map;
String npath = path+"."+property.getName(); return parse(object);
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);
}
}
private String describeType(JsonElement e) { public Element parse(JsonObject object) throws FHIRException {
if (e.isJsonArray()) JsonElement rt = object.get("resourceType");
return "an Array"; if (rt == null) {
if (e.isJsonObject()) 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<String, JsonElement> 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<Property> properties = element.getProperty().getChildProperties(element.getName(), null);
Set<String> processed = new HashSet<String>();
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<String, JsonElement> 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<String> 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<String> 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"; return "an Object";
if (e.isJsonPrimitive()) if (e.isJsonPrimitive())
return "a primitive property"; return "a primitive property";
@ -246,24 +249,25 @@ public class JsonParser extends ParserBase {
return null; return null;
} }
private void parseChildComplexInstance(String npath, JsonObject object, Element element, Property property, String name, JsonElement e) throws FHIRException { private void parseChildComplexInstance(String npath, String fpath, JsonObject object, Element element, Property property, String name, JsonElement e) throws FHIRException {
if (e instanceof JsonObject) { if (e instanceof JsonObject) {
JsonObject child = (JsonObject) e; JsonObject child = (JsonObject) e;
Element n = new Element(name, property).markLocation(line(child), col(child)); Element n = new Element(name, property).markLocation(line(child), col(child));
checkObject(child, npath); n.setPath(fpath);
element.getChildren().add(n); checkObject(child, npath);
if (property.isResource()) element.getChildren().add(n);
parseResource(npath, child, n, property); if (property.isResource())
else parseResource(npath, child, n, property);
parseChildren(npath, child, n, false); else
} else parseChildren(npath, child, n, false);
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); } 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) { private String describe(JsonElement e) {
if (e instanceof JsonArray) { if (e instanceof JsonArray) {
return "an array"; return "an array";
} }
if (e instanceof JsonObject) { if (e instanceof JsonObject) {
return "an object"; return "an object";
} }
@ -274,20 +278,21 @@ public class JsonParser extends ParserBase {
} }
private void parseChildPrimitive(JsonObject object, Element element, Set<String> processed, Property property, String path, String name) throws FHIRException { private void parseChildPrimitive(JsonObject object, Element element, Set<String> processed, Property property, String path, String name) throws FHIRException {
String npath = path+"."+property.getName(); String npath = path+"."+property.getName();
processed.add(name); String fpath = element.getPath()+"."+property.getName();
processed.add("_"+name); processed.add(name);
JsonElement main = object.has(name) ? object.get(name) : null; processed.add("_"+name);
JsonElement fork = object.has("_"+name) ? object.get("_"+name) : null; JsonElement main = object.has(name) ? object.get(name) : null;
if (main != null || fork != null) { JsonElement fork = object.has("_"+name) ? object.get("_"+name) : null;
if (property.isList()) { if (main != null || fork != null) {
boolean ok = true; if (property.isList()) {
if (!(main == null || main instanceof JsonArray)) { boolean ok = true;
logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main), name, path), IssueSeverity.ERROR); if (!(main == null || main instanceof JsonArray)) {
ok = false; 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)) { 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; ok = false;
} }
if (ok) { if (ok) {
@ -296,158 +301,158 @@ public class JsonParser extends ParserBase {
for (int i = 0; i < Math.max(arrC(arr1), arrC(arr2)); i++) { for (int i = 0; i < Math.max(arrC(arr1), arrC(arr2)); i++) {
JsonElement m = arrI(arr1, i); JsonElement m = arrI(arr1, i);
JsonElement f = arrI(arr2, i); JsonElement f = arrI(arr2, i);
parseChildPrimitiveInstance(element, property, name, npath, m, f); parseChildPrimitiveInstance(element, property, name, npath, fpath, m, f);
} }
} }
} else { } else {
parseChildPrimitiveInstance(element, property, name, npath, main, fork); parseChildPrimitiveInstance(element, property, name, npath, fpath, main, fork);
} }
} }
} }
private JsonElement arrI(JsonArray arr, int i) { private JsonElement arrI(JsonArray arr, int i) {
return arr == null || i >= arr.size() || arr.get(i) instanceof JsonNull ? null : arr.get(i); return arr == null || i >= arr.size() || arr.get(i) instanceof JsonNull ? null : arr.get(i);
} }
private int arrC(JsonArray arr) { private int arrC(JsonArray arr) {
return arr == null ? 0 : arr.size(); return arr == null ? 0 : arr.size();
} }
private void parseChildPrimitiveInstance(Element element, Property property, String name, String npath, private void parseChildPrimitiveInstance(Element element, Property property, String name, String npath, String fpath, JsonElement main, JsonElement fork) throws FHIRException {
JsonElement main, JsonElement fork) throws FHIRException { if (main != null && !(main instanceof JsonPrimitive))
if (main != null && !(main instanceof JsonPrimitive)) logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(
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); I18nConstants.THIS_PROPERTY_MUST_BE_AN_SIMPLE_VALUE_NOT_, describe(main), name, npath), IssueSeverity.ERROR);
else if (fork != null && !(fork instanceof JsonObject)) 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); 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 { else {
Element n = new Element(name, property).markLocation(line(main != null ? main : fork), col(main != null ? main : fork)); Element n = new Element(name, property).markLocation(line(main != null ? main : fork), col(main != null ? main : fork));
element.getChildren().add(n); n.setPath(fpath);
if (main != null) { element.getChildren().add(n);
JsonPrimitive p = (JsonPrimitive) main; if (main != null) {
if (p.isNumber() && p.getAsNumber() instanceof JsonTrackingParser.PresentedBigDecimal) { JsonPrimitive p = (JsonPrimitive) main;
String rawValue = ((JsonTrackingParser.PresentedBigDecimal) p.getAsNumber()).getPresentation(); if (p.isNumber() && p.getAsNumber() instanceof JsonTrackingParser.PresentedBigDecimal) {
n.setValue(rawValue); String rawValue = ((JsonTrackingParser.PresentedBigDecimal) p.getAsNumber()).getPresentation();
} else { n.setValue(rawValue);
n.setValue(p.getAsString()); } 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 { if (policy == ValidationPolicy.EVERYTHING) {
n.setXhtml(new XhtmlParser().setValidatorMode(policy == ValidationPolicy.EVERYTHING).parse(n.getValue(), null).getDocumentElement()); // now we cross-check the primitive format against the stated type
} catch (Exception e) { if (Utilities.existsInList(n.getType(), "boolean")) {
logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_XHTML_, e.getMessage()), IssueSeverity.ERROR); 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 (policy == ValidationPolicy.EVERYTHING) { if (!p.isNumber())
// now we cross-check the primitive format against the stated type logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_NUMBER), IssueSeverity.ERROR);
if (Utilities.existsInList(n.getType(), "boolean")) { } else if (!p.isString())
if (!p.isBoolean()) logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_STRING), IssueSeverity.ERROR);
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()) if (fork != null) {
logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_NUMBER), IssueSeverity.ERROR); JsonObject child = (JsonObject) fork;
} else if (!p.isString()) checkObject(child, npath);
logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_STRING), IssueSeverity.ERROR); parseChildren(npath, child, n, false);
} }
} }
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 { private void parseResource(String npath, JsonObject res, Element parent, Property elementProperty) throws FHIRException {
JsonElement rt = res.get("resourceType"); JsonElement rt = res.get("resourceType");
if (rt == null) { if (rt == null) {
logError(line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); logError(line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL);
} else { } else {
String name = rt.getAsString(); String name = rt.getAsString();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs())); StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs()));
if (sd == null) { 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); 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 { } else {
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty); parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name); parent.setType(name);
parseChildren(npath, res, parent, true); parseChildren(npath, res, parent, true);
} }
} }
} }
private void reapComments(JsonObject object, Element context) { private void reapComments(JsonObject object, Element context) {
if (object.has("fhir_comments")) { if (object.has("fhir_comments")) {
JsonArray arr = object.getAsJsonArray("fhir_comments"); JsonArray arr = object.getAsJsonArray("fhir_comments");
for (JsonElement e : arr) { for (JsonElement e : arr) {
context.getComments().add(e.getAsString()); context.getComments().add(e.getAsString());
} }
} }
} }
private int line(JsonElement e) { private int line(JsonElement e) {
if (map == null|| !map.containsKey(e)) if (map == null|| !map.containsKey(e))
return -1; return -1;
else else
return map.get(e).getLine(); return map.get(e).getLine();
} }
private int col(JsonElement e) { private int col(JsonElement e) {
if (map == null|| !map.containsKey(e)) if (map == null|| !map.containsKey(e))
return -1; return -1;
else else
return map.get(e).getCol(); 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); json.link(link);
if (name != null) if (name != null)
json.name(name); json.name(name);
json.value(value); json.value(value);
} }
protected void open(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.beginObject();
}
protected void close() throws IOException {
json.endObject();
}
protected void openArray(String name, String link) throws IOException {
json.link(link); json.link(link);
if (name != null) if (name != null)
json.name(name); json.name(name);
json.beginArray(); json.beginObject();
} }
protected void closeArray() throws IOException { protected void close() throws IOException {
json.endArray(); 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 @Override
public void compose(Element e, OutputStream stream, OutputStyle style, String identity) throws FHIRException, IOException { public void compose(Element e, OutputStream stream, OutputStyle style, String identity) throws FHIRException, IOException {
OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
if (style == OutputStyle.CANONICAL) if (style == OutputStyle.CANONICAL)
json = new JsonCreatorCanonical(osw); json = new JsonCreatorCanonical(osw);
else else
json = new JsonCreatorGson(osw); json = new JsonCreatorGson(osw);
json.setIndent(style == OutputStyle.PRETTY ? " " : ""); json.setIndent(style == OutputStyle.PRETTY ? " " : "");
json.beginObject(); json.beginObject();
prop("resourceType", e.getType(), null); prop("resourceType", e.getType(), null);
Set<String> done = new HashSet<String>(); Set<String> done = new HashSet<String>();
for (Element child : e.getChildren()) { for (Element child : e.getChildren()) {
compose(e.getName(), e, done, child); compose(e.getName(), e, done, child);
} }
json.endObject(); json.endObject();
json.finish(); json.finish();
osw.flush(); osw.flush();
} }
public void compose(Element e, JsonCreator json) throws Exception { public void compose(Element e, JsonCreator json) throws Exception {
this.json = json; this.json = json;
@ -462,104 +467,104 @@ public class JsonParser extends ParserBase {
json.finish(); json.finish();
} }
private void compose(String path, Element e, Set<String> done, Element child) throws IOException { private void compose(String path, Element e, Set<String> done, Element child) throws IOException {
boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList(); boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList();
if (!isList) {// for specials, ignore the cardinality of the stated type if (!isList) {// for specials, ignore the cardinality of the stated type
compose(path, child); compose(path, child);
} else if (!done.contains(child.getName())) { } else if (!done.contains(child.getName())) {
done.add(child.getName()); done.add(child.getName());
List<Element> list = e.getChildrenByName(child.getName()); List<Element> list = e.getChildrenByName(child.getName());
composeList(path, list); composeList(path, list);
} }
} }
private void composeList(String path, List<Element> list) throws IOException { private void composeList(String path, List<Element> list) throws IOException {
// there will be at least one element // there will be at least one element
String name = list.get(0).getName(); String name = list.get(0).getName();
boolean complex = true; boolean complex = true;
if (list.get(0).isPrimitive()) { if (list.get(0).isPrimitive()) {
boolean prim = false; boolean prim = false;
complex = false; complex = false;
for (Element item : list) { for (Element item : list) {
if (item.hasValue()) if (item.hasValue())
prim = true; prim = true;
if (item.hasChildren()) if (item.hasChildren())
complex = true; complex = true;
} }
if (prim) { if (prim) {
openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty()));
for (Element item : list) { for (Element item : list) {
if (item.hasValue()) if (item.hasValue())
primitiveValue(null, item); primitiveValue(null, item);
else else
json.nullValue(); json.nullValue();
} }
closeArray(); closeArray();
} }
name = "_"+name; name = "_"+name;
} }
if (complex) { if (complex) {
openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty()));
for (Element item : list) { for (Element item : list) {
if (item.hasChildren()) { if (item.hasChildren()) {
open(null,null); open(null,null);
if (item.getProperty().isResource()) { if (item.getProperty().isResource()) {
prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType())); prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType()));
} }
Set<String> done = new HashSet<String>(); Set<String> done = new HashSet<String>();
for (Element child : item.getChildren()) { for (Element child : item.getChildren()) {
compose(path+"."+name+"[]", item, done, child); compose(path+"."+name+"[]", item, done, child);
} }
close(); close();
} else } else
json.nullValue(); json.nullValue();
} }
closeArray(); closeArray();
} }
} }
private void primitiveValue(String name, Element item) throws IOException { private void primitiveValue(String name, Element item) throws IOException {
if (name != null) { if (name != null) {
if (linkResolver != null) if (linkResolver != null)
json.link(linkResolver.resolveProperty(item.getProperty())); json.link(linkResolver.resolveProperty(item.getProperty()));
json.name(name); json.name(name);
} }
String type = item.getType(); String type = item.getType();
if (Utilities.existsInList(type, "boolean")) if (Utilities.existsInList(type, "boolean"))
json.value(item.getValue().trim().equals("true") ? new Boolean(true) : new Boolean(false)); json.value(item.getValue().trim().equals("true") ? new Boolean(true) : new Boolean(false));
else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt"))
json.value(new Integer(item.getValue())); json.value(new Integer(item.getValue()));
else if (Utilities.existsInList(type, "decimal")) else if (Utilities.existsInList(type, "decimal"))
try { try {
json.value(new BigDecimal(item.getValue())); json.value(new BigDecimal(item.getValue()));
} catch (Exception e) { } catch (Exception e) {
throw new NumberFormatException(context.formatMessage(I18nConstants.ERROR_WRITING_NUMBER__TO_JSON, item.getValue())); throw new NumberFormatException(context.formatMessage(I18nConstants.ERROR_WRITING_NUMBER__TO_JSON, item.getValue()));
} }
else else
json.value(item.getValue()); json.value(item.getValue());
} }
private void compose(String path, Element element) throws IOException { private void compose(String path, Element element) throws IOException {
String name = element.getName(); String name = element.getName();
if (element.isPrimitive() || isPrimitive(element.getType())) { if (element.isPrimitive() || isPrimitive(element.getType())) {
if (element.hasValue()) if (element.hasValue())
primitiveValue(name, element); primitiveValue(name, element);
name = "_"+name; name = "_"+name;
if (element.getType().equals("xhtml")) if (element.getType().equals("xhtml"))
json.anchor("end-xhtml"); json.anchor("end-xhtml");
} }
if (element.hasChildren()) { if (element.hasChildren()) {
open(name, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); open(name, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()));
if (element.getProperty().isResource()) { if (element.getProperty().isResource()) {
prop("resourceType", element.getType(), linkResolver == null ? null : linkResolver.resolveType(element.getType())); prop("resourceType", element.getType(), linkResolver == null ? null : linkResolver.resolveType(element.getType()));
} }
Set<String> done = new HashSet<String>(); Set<String> done = new HashSet<String>();
for (Element child : element.getChildren()) { for (Element child : element.getChildren()) {
compose(path+"."+element.getName(), element, done, child); compose(path+"."+element.getName(), element, done, child);
} }
close(); close();
} }
} }
public boolean isAllowComments() { public boolean isAllowComments() {
return allowComments; return allowComments;
@ -570,5 +575,5 @@ public class JsonParser extends ParserBase {
return this; return this;
} }
} }

View File

@ -228,9 +228,13 @@ public class Property {
return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE); return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE);
} }
public boolean isList() { public boolean isList() {
return !"1".equals(definition.getMax()); return !"1".equals(definition.getMax());
} }
public boolean isBaseList() {
return !"1".equals(definition.getBase().getMax());
}
public String getScopedPropertyName() { public String getScopedPropertyName() {
return definition.getBase().getPath(); return definition.getBase().getPath();

View File

@ -203,6 +203,7 @@ public class XmlParser extends ParserBase {
return null; return null;
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)); Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd));
result.setPath(element.getLocalName());
checkElement(element, path, result.getProperty()); checkElement(element, path, result.getProperty());
result.markLocation(line(element), col(element)); result.markLocation(line(element), col(element));
result.setType(element.getLocalName()); 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 { public Element parse(org.w3c.dom.Element base, String type) throws Exception {
StructureDefinition sd = getDefinition(0, 0, FormatUtilities.FHIR_NS, type); StructureDefinition sd = getDefinition(0, 0, FormatUtilities.FHIR_NS, type);
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)); 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(); String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
checkElement(base, path, result.getProperty()); checkElement(base, path, result.getProperty());
result.setType(base.getLocalName()); result.setType(base.getLocalName());
@ -283,13 +285,18 @@ public class XmlParser extends ParserBase {
if (property != null) { 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 ("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"))) { 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 { } 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 { } else {
element.getChildren().add( Element n = new Element(property.getName(), property, property.getType(), text).markLocation(line, col);
new Element(property.getName(), property, property.getType(), text).markLocation(line, col)); n.setPath(element.getPath()+"."+property.getName());
element.getChildren().add(n);
} }
} }
else { 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); av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av);
if (property.getName().equals("value") && element.isPrimitive()) if (property.getName().equals("value") && element.isPrimitive())
element.setValue(av); element.setValue(av);
else else {
element.getChildren().add(new Element(property.getName(), property, property.getType(), av).markLocation(line, col)); Element n = new Element(property.getName(), property, property.getType(), av).markLocation(line, col);
n.setPath(element.getPath()+"."+property.getName());
element.getChildren().add(n);
}
} else { } else {
boolean ok = false; boolean ok = false;
if (FormatUtilities.FHIR_NS.equals(node.getNamespaceURI())) { 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(); Node child = node.getFirstChild();
while (child != null) { while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) { if (child.getNodeType() == Node.ELEMENT_NODE) {
Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI()); Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI());
if (property != null) { if (property != null) {
if (property.getName().equals(lastName)) {
repeatCount++;
} else {
lastName = property.getName();
repeatCount = 0;
}
if (!property.isChoice() && "xhtml".equals(property.getType())) { if (!property.isChoice() && "xhtml".equals(property.getType())) {
XhtmlNode xhtml; XhtmlNode xhtml;
if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT)) if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT))
xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child); xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child);
else else
xhtml = new XhtmlParser().setValidatorMode(true).parseHtmlNode((org.w3c.dom.Element) child); 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 { } else {
String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName(); String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName();
Element n = new Element(child.getLocalName(), property).markLocation(line(child), col(child)); 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()); checkElement((org.w3c.dom.Element) child, npath, n.getProperty());
boolean ok = true; boolean ok = true;
if (property.isChoice()) { if (property.isChoice()) {

View File

@ -132,7 +132,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
li.tx(opt.getValue().primitiveValue()); li.tx(opt.getValue().primitiveValue());
} else if (opt.getValue() instanceof Coding) { } else if (opt.getValue() instanceof Coding) {
Coding c = (Coding) opt.getValue(); 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) { if (link == null) {
li.tx(c.getSystem()+"#"+c.getCode()); li.tx(c.getSystem()+"#"+c.getCode());
} else { } else {

View File

@ -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_NOTSLICE = "Validation_VAL_Profile_NotSlice";
public static final String VALIDATION_VAL_PROFILE_NOTYPE = "Validation_VAL_Profile_NoType"; 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_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 = "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_GLOBAL = "VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL";
public static final String VALIDATION_VAL_PROFILE_SIGNPOST_META = "VALIDATION_VAL_PROFILE_SIGNPOST_META"; public static final String VALIDATION_VAL_PROFILE_SIGNPOST_META = "VALIDATION_VAL_PROFILE_SIGNPOST_META";

View File

@ -790,8 +790,9 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
return signpost; return signpost;
} }
public void setSignpost(boolean signpost) { public ValidationMessage setSignpost(boolean signpost) {
this.signpost = signpost; this.signpost = signpost;
return this;
} }

View File

@ -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 = 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} 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_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 = 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_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} 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) 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} 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_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_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_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 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 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

View File

@ -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} 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_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_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_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 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 TERMINOLOGY_TX_NOSVC_BOUND_REQ = Kan niet bevestigen dat de gevonden codes bestaan in de verplichte waardelijst {0} omdat er geen terminologieservice is

View File

@ -284,12 +284,9 @@ public class BaseValidator {
return thePass; return thePass;
} }
protected boolean signpost(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { protected ValidationMessage signpost(List<ValidationMessage> errors, IssueType type, int line, int col, String path, String theMessage, Object... theMessageArguments) {
if (!thePass) { String message = context.formatMessage(theMessage, theMessageArguments);
String message = context.formatMessage(theMessage, theMessageArguments); return addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage).setSignpost(true);
addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION, theMessage).setSignpost(true);
}
return thePass;
} }
protected boolean txHint(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { protected boolean txHint(List<ValidationMessage> errors, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {

View File

@ -3270,8 +3270,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rr.setResource(nstack.getElement()); rr.setResource(nstack.getElement());
rr.setFocus(nstack.getElement()); rr.setFocus(nstack.getElement());
rr.setExternal(false); rr.setExternal(false);
rr.setStack(nstack.push(nstack.getElement(), -1, nstack.getElement().getProperty().getDefinition(), nstack.getElement().getProperty().getDefinition())); rr.setStack(nstack);
rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")"); // rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")");
System.out.println("-->"+nstack.getLiteralPath());
return rr; return rr;
} }
if (nstack.getElement().getSpecial() == SpecialElement.CONTAINED) { 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) // 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<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { private void start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException {
checkLang(resource, stack); 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())) { if (BUNDLE.equals(element.fhirType())) {
resolveBundleReferences(element, new ArrayList<Element>()); resolveBundleReferences(element, new ArrayList<Element>());
@ -3996,7 +3999,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
if (sd != null) { 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(); stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false); startInner(hostContext, errors, resource, element, sd, stack, false);
} }
@ -4011,7 +4016,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (rt.equals(gl.getType())) { if (rt.equals(gl.getType())) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, gl.getProfile()); 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())) { 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(); stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false); startInner(hostContext, errors, resource, element, sd, stack, false);
} }
@ -4517,13 +4524,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
type = null; 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) { // if (debug) {
// System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()); // 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(); 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; boolean thisIsCodeableConcept = false;
String thisExtension = null; String thisExtension = null;
boolean checkDisplay = true; boolean checkDisplay = true;

View File

@ -112,7 +112,9 @@ public class BundleValidator extends BaseValidator{
} else { } else {
Element res = entry.getNamedChild(RESOURCE); Element res = entry.getNamedChild(RESOURCE);
NodeStack rstack = estack.push(res, -1, null, null); 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(); stack.resetIds();
validator.startInner(hostContext, errors, res, res, defn, rstack, false); validator.startInner(hostContext, errors, res, res, defn, rstack, false);
} }

View File

@ -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)) { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
StructureDefinition t = determineBaseType(sd); StructureDefinition t = determineBaseType(sd);
if (t == null) { if (t == null) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
throw new Error("What to do about this?");
} else { } else {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); 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)) { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
StructureDefinition t = determineBaseType(sd); StructureDefinition t = determineBaseType(sd);
if (t == null) { if (t == null) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
throw new Error("What to do about this?");
} else { } else {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); 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) { if (t == null) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
} else if (!VersionUtilities.isR5Ver(context.getVersion())) { } 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 { } 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"); 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");
} }

View File

@ -70,9 +70,12 @@ public class ChildIterator {
String nb = cursor == 0 ? "--" : parent.getChildren().get(cursor - 1).getName(); String nb = cursor == 0 ? "--" : parent.getChildren().get(cursor - 1).getName();
String na = cursor >= parent.getChildren().size() - 1 ? "--" : 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)) { if (name().equals(nb) || name().equals(na)) {
return lastCount; return lastCount;
} else } else if (element().isBaseList()) {
return -1; return 0;
} else {
return -1;
}
} }
public boolean next() { public boolean next() {

View File

@ -34,7 +34,7 @@ public class NodeStack {
this.context = context; this.context = context;
ids = new HashMap<>(); ids = new HashMap<>();
this.element = element; this.element = element;
literalPath = element.getName(); literalPath = element.getPath();
workingLang = validationLanguage; workingLang = validationLanguage;
if (!element.getName().equals(element.fhirType())) { if (!element.getName().equals(element.fhirType())) {
logicalPaths = new ArrayList<>(); logicalPaths = new ArrayList<>();