Add path and message tracking to element model

This commit is contained in:
Grahame Grieve 2021-06-23 20:35:15 +10:00
parent 91de7ec65b
commit a2c6dd6af1
4 changed files with 474 additions and 407 deletions

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.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<ValidationMessage> 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<ValidationMessage> getMessages() {
return messages;
}
}

View File

@ -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<JsonElement, LocationData> map;
private boolean allowComments;
private JsonCreator json;
private Map<JsonElement, LocationData> 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<JsonElement, LocationData>();
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<JsonElement, LocationData>();
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<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());
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<String> 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<JsonElement, LocationData> 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<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";
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<String> 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<String> done = new HashSet<String>();
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<String> done = new HashSet<String>();
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<String> 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<Element> list = e.getChildrenByName(child.getName());
composeList(path, list);
}
}
private void compose(String path, Element e, Set<String> 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<Element> list = e.getChildrenByName(child.getName());
composeList(path, list);
}
}
private void composeList(String path, List<Element> 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<String> done = new HashSet<String>();
for (Element child : item.getChildren()) {
compose(path+"."+name+"[]", item, done, child);
}
close();
} else
json.nullValue();
}
closeArray();
}
}
private void composeList(String path, List<Element> 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<String> done = new HashSet<String>();
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<String> done = new HashSet<String>();
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<String> done = new HashSet<String>();
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;
}
}

View File

@ -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();

View File

@ -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()) {