more work on sql-on-fhir implementation
This commit is contained in:
parent
41eaad7356
commit
1e98e69338
|
@ -132,6 +132,7 @@ public class Element extends Base implements NamedItem {
|
|||
private Base source;
|
||||
private boolean ignorePropertyOrder;
|
||||
private FhirFormat format;
|
||||
private Object nativeObject;
|
||||
|
||||
public Element(String name) {
|
||||
super();
|
||||
|
@ -1478,4 +1479,13 @@ public class Element extends Base implements NamedItem {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Object getNativeObject() {
|
||||
return nativeObject;
|
||||
}
|
||||
|
||||
public Element setNativeObject(Object nativeObject) {
|
||||
this.nativeObject = nativeObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -193,6 +193,7 @@ public class JsonParser extends ParserBase {
|
|||
}
|
||||
|
||||
private void checkObject(List<ValidationMessage> errors, JsonObject object, Element b, String path) {
|
||||
b.setNativeObject(object);
|
||||
checkComments(errors, object, b, path);
|
||||
if (policy == ValidationPolicy.EVERYTHING) {
|
||||
if (object.getProperties().size() == 0) {
|
||||
|
|
|
@ -224,6 +224,12 @@ public abstract class ParserBase {
|
|||
new ContextUtilities(context).generateSnapshot(sd);
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
|
||||
if (name.equals(sd.getUrl()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
|
||||
new ContextUtilities(context).generateSnapshot(sd);
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line, col, name, IssueType.STRUCTURE, context.formatMessage(I18nConstants.THIS_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL);
|
||||
return null;
|
||||
|
|
|
@ -302,7 +302,7 @@ public class XmlParser extends ParserBase {
|
|||
|
||||
public Element parse(List<ValidationMessage> errors, org.w3c.dom.Element base, String type) throws Exception {
|
||||
StructureDefinition sd = getDefinition(errors, 0, 0, FormatUtilities.FHIR_NS, type);
|
||||
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
|
||||
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML).setNativeObject(base);
|
||||
result.setPath(base.getLocalName());
|
||||
String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
|
||||
checkElement(errors, base, path, result.getProperty(), false);
|
||||
|
@ -431,7 +431,7 @@ public class XmlParser extends ParserBase {
|
|||
}
|
||||
}
|
||||
}
|
||||
Element n = new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML);
|
||||
Element n = new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child);
|
||||
n.setPath(element.getPath()+"."+property.getName());
|
||||
element.getChildren().add(n);
|
||||
} else {
|
||||
|
@ -440,7 +440,7 @@ public class XmlParser extends ParserBase {
|
|||
if (!property.isChoice() && !name.equals(property.getName())) {
|
||||
name = property.getName();
|
||||
}
|
||||
Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML);
|
||||
Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child);
|
||||
if (property.isList()) {
|
||||
n.setPath(element.getPath()+"."+property.getName()+"["+repeatCount+"]");
|
||||
} else {
|
||||
|
@ -497,7 +497,7 @@ public class XmlParser extends ParserBase {
|
|||
|
||||
npath = npath+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName();
|
||||
name = child.getLocalName();
|
||||
Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML);
|
||||
Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child);
|
||||
cgn.getChildren().add(n);
|
||||
n.setPath(element.getPath()+"."+property.getName());
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty(), false);
|
||||
|
@ -524,7 +524,7 @@ public class XmlParser extends ParserBase {
|
|||
|
||||
npath = npath+"/text()";
|
||||
name = mtProp.getName();
|
||||
Element n = new Element(name, mtProp, mtProp.getType(), child.getTextContent().trim()).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML);
|
||||
Element n = new Element(name, mtProp, mtProp.getType(), child.getTextContent().trim()).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML).setNativeObject(child);
|
||||
cgn.getChildren().add(n);
|
||||
n.setPath(element.getPath()+"."+mtProp.getName());
|
||||
|
||||
|
|
|
@ -111,7 +111,11 @@ public class Runner implements IEvaluationContext {
|
|||
}
|
||||
if (ok) {
|
||||
List<List<Cell>> rows = new ArrayList<>();
|
||||
generateCells(b, vd, rows);
|
||||
rows.add(new ArrayList<Cell>());
|
||||
|
||||
for (JsonObject select : vd.getJsonObjects("select")) {
|
||||
executeSelect(select, b, rows);
|
||||
}
|
||||
for (List<Cell> row : rows) {
|
||||
storage.addRow(store, row);
|
||||
}
|
||||
|
@ -119,48 +123,47 @@ public class Runner implements IEvaluationContext {
|
|||
}
|
||||
storage.finish(store);
|
||||
}
|
||||
|
||||
private void generateCells(Base bl, JsonObject vd, List<List<Cell>> rows) {
|
||||
if (vd.has("forEach")) {
|
||||
executeForEach(vd, bl, rows);
|
||||
} else if (vd.has("forEachOrNull")) {
|
||||
executeForEachOrNull(vd, bl, rows);
|
||||
} else if (vd.has("union")) {
|
||||
executeUnion(vd, bl, rows);
|
||||
} else {
|
||||
for (JsonObject select : vd.getJsonObjects("select")) {
|
||||
executeSelect(select, bl, rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSelect(JsonObject select, Base bl, List<List<Cell>> rows) {
|
||||
if (select.has("path")) {
|
||||
executeSelectPath(select, bl, rows);
|
||||
} else if (select.has("forEach")) {
|
||||
executeForEach(select, bl, rows);
|
||||
private void executeSelect(JsonObject select, Base b, List<List<Cell>> rows) {
|
||||
List<Base> focus = new ArrayList<>();
|
||||
|
||||
if (select.has("forEach")) {
|
||||
focus.addAll(executeForEach(select, b));
|
||||
} else if (select.has("forEachOrNull")) {
|
||||
executeForEachOrNull(select, bl, rows);
|
||||
} else if (select.has("union")) {
|
||||
executeUnion(select, bl, rows);
|
||||
focus.addAll(executeForEachOrNull(select, b));
|
||||
} else {
|
||||
focus.add(b);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeForEach(JsonObject focus, Base b, List<List<Cell>> rows) {
|
||||
ExpressionNode n = (ExpressionNode) focus.getUserData("forEach");
|
||||
List<Base> bl2 = fpe.evaluate(b, n);
|
||||
// } else if (select.has("union")) {
|
||||
// focus.addAll(executeUnion(select, b));
|
||||
|
||||
List<List<Cell>> tempRows = new ArrayList<>();
|
||||
tempRows.addAll(rows);
|
||||
rows.clear();
|
||||
for (Base b2 : bl2) {
|
||||
List<List<Cell>> rowsToAdd = cloneRows(tempRows);
|
||||
for (JsonObject select : focus.getJsonObjects("select")) {
|
||||
executeSelect(select, b2, rowsToAdd);
|
||||
|
||||
for (Base f : focus) {
|
||||
List<List<Cell>> rowsToAdd = cloneRows(tempRows);
|
||||
|
||||
for (JsonObject column : select.getJsonObjects("column")) {
|
||||
executeColumn(column, f, rowsToAdd);
|
||||
}
|
||||
|
||||
for (JsonObject sub : select.getJsonObjects("union")) {
|
||||
executeSelect(sub, f, rowsToAdd);
|
||||
}
|
||||
|
||||
for (JsonObject sub : select.getJsonObjects("select")) {
|
||||
executeSelect(sub, f, rowsToAdd);
|
||||
}
|
||||
rows.addAll(rowsToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Base> executeUnion(JsonObject focus, Base b, List<List<Cell>> rows) {
|
||||
throw new FHIRException("union is not supported");
|
||||
}
|
||||
|
||||
private List<List<Cell>> cloneRows(List<List<Cell>> rows) {
|
||||
List<List<Cell>> list = new ArrayList<>();
|
||||
for (List<Cell> row : rows) {
|
||||
|
@ -176,30 +179,38 @@ public class Runner implements IEvaluationContext {
|
|||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void executeForEachOrNull(JsonObject focus, Base b, List<List<Cell>> rows) {
|
||||
throw new FHIRException("forEachOrNull is not supported");
|
||||
|
||||
private List<Base> executeForEach(JsonObject focus, Base b) {
|
||||
ExpressionNode n = (ExpressionNode) focus.getUserData("forEach");
|
||||
List<Base> result = new ArrayList<>();
|
||||
result.addAll(fpe.evaluate(b, n));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void executeUnion(JsonObject focus, Base b, List<List<Cell>> rows) {
|
||||
throw new FHIRException("union is not supported");
|
||||
private List<Base> executeForEachOrNull(JsonObject focus, Base b) {
|
||||
ExpressionNode n = (ExpressionNode) focus.getUserData("forEachOrNull");
|
||||
List<Base> result = new ArrayList<>();
|
||||
result.addAll(fpe.evaluate(b, n));
|
||||
if (result.size() == 0) {
|
||||
result.add(null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private void executeSelectPath(JsonObject select, Base b, List<List<Cell>> rows) {
|
||||
ExpressionNode n = (ExpressionNode) select.getUserData("path");
|
||||
List<Base> bl2 = fpe.evaluate(b, n);
|
||||
String name = select.getUserString("name");
|
||||
if (!bl2.isEmpty()) {
|
||||
if (rows.isEmpty()) {
|
||||
rows.add(new ArrayList<Cell>());
|
||||
}
|
||||
for (List<Cell> row : rows) {
|
||||
Cell c = cell(row, name);
|
||||
if (c == null) {
|
||||
c = new Cell(column(name));
|
||||
row.add(c);
|
||||
}
|
||||
private void executeColumn(JsonObject column, Base b, List<List<Cell>> rows) {
|
||||
ExpressionNode n = (ExpressionNode) column.getUserData("path");
|
||||
List<Base> bl2 = new ArrayList<>();
|
||||
if (b != null) {
|
||||
bl2.addAll(fpe.evaluate(b, n));
|
||||
}
|
||||
String name = column.getUserString("name");
|
||||
for (List<Cell> row : rows) {
|
||||
Cell c = cell(row, name);
|
||||
if (c == null) {
|
||||
c = new Cell(column(name));
|
||||
row.add(c);
|
||||
}
|
||||
if (!bl2.isEmpty()) {
|
||||
if (bl2.size() + c.getValues().size() > 1) {
|
||||
// this is a problem if collection != true or if the storage can't deal with it
|
||||
// though this should've been picked up before now - but there are circumstances where it wouldn't be
|
||||
|
@ -211,14 +222,6 @@ public class Runner implements IEvaluationContext {
|
|||
c.getValues().add(genValue(c.getColumn(), b2));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (List<Cell> row : rows) {
|
||||
Cell c = cell(row, name);
|
||||
if (c == null) {
|
||||
c = new Cell(column(name));
|
||||
row.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,6 +379,9 @@ public class Runner implements IEvaluationContext {
|
|||
String rt = null;
|
||||
if (parameters.size() > 0) {
|
||||
rt = parameters.get(0).get(0).primitiveValue();
|
||||
if (rt.startsWith("FHIR.")) {
|
||||
rt = rt.substring(5);
|
||||
}
|
||||
}
|
||||
List<Base> base = new ArrayList<Base>();
|
||||
if (focus.size() == 1) {
|
||||
|
@ -427,6 +433,10 @@ public class Runner implements IEvaluationContext {
|
|||
public ValueSet resolveValueSet(Object appContext, String url) {
|
||||
throw new Error("Not implemented yet: resolveValueSet");
|
||||
}
|
||||
@Override
|
||||
public boolean paramIsType(String name, int index) {
|
||||
return "getReferenceKey".equals(name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.r5.utils.sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -24,20 +25,20 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
|||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
|
||||
public class Validator {
|
||||
|
||||
|
||||
private IWorkerContext context;
|
||||
private FHIRPathEngine fpe;
|
||||
private List<String> prohibitedNames = new ArrayList<String>();
|
||||
private List<ValidationMessage> issues = new ArrayList<ValidationMessage>();
|
||||
private boolean arrays;
|
||||
private boolean complexTypes;
|
||||
private boolean needsName;
|
||||
private Boolean arrays;
|
||||
private Boolean complexTypes;
|
||||
private Boolean needsName;
|
||||
|
||||
private String resourceName;
|
||||
private List<Column> columns = new ArrayList<Column>();
|
||||
private String name;
|
||||
|
||||
protected Validator(IWorkerContext context, FHIRPathEngine fpe, List<String> prohibitedNames, boolean arrays, boolean complexTypes, boolean needsName) {
|
||||
public Validator(IWorkerContext context, FHIRPathEngine fpe, List<String> prohibitedNames, Boolean arrays, Boolean complexTypes, Boolean needsName) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.fpe = fpe;
|
||||
|
@ -58,7 +59,9 @@ public class Validator {
|
|||
public void checkViewDefinition(String path, JsonObject viewDefinition) {
|
||||
JsonElement nameJ = viewDefinition.get("name");
|
||||
if (nameJ == null) {
|
||||
if (needsName) {
|
||||
if (needsName == null) {
|
||||
hint(path, viewDefinition, "No name provided. A name is required in many contexts where a ViewDefinition is used");
|
||||
} else if (needsName) {
|
||||
error(path, viewDefinition, "No name provided", IssueType.REQUIRED);
|
||||
}
|
||||
} else if (!(nameJ instanceof JsonString)) {
|
||||
|
@ -122,119 +125,201 @@ public class Validator {
|
|||
}
|
||||
|
||||
private void checkSelect(String path, JsonObject select, TypeDetails t) {
|
||||
if (select.has("path")) {
|
||||
checkSelectPath(path, select, select.get("path"), t);
|
||||
} else if (select.has("forEach")) {
|
||||
checkForEach(path, select, select.get("forEach"), t);
|
||||
|
||||
if (select.has("forEach")) {
|
||||
t = checkForEach(path, select, select.get("forEach"), t);
|
||||
} else if (select.has("forEachOrNull")) {
|
||||
checkForEachOrNull(path, select, select.get("forEachOrNull"), t);
|
||||
} else if (select.has("union")) {
|
||||
checkUnion(path, select, select.get("union"), t);
|
||||
} else {
|
||||
error(path, select, "The select has neither a path, forEach, forEachOrNull, or union statement", IssueType.REQUIRED);
|
||||
t = checkForEachOrNull(path, select, select.get("forEachOrNull"), t);
|
||||
}
|
||||
|
||||
if (t != null) {
|
||||
boolean content = false;
|
||||
|
||||
if (select.has("union")) {
|
||||
content = checkUnion(path, select, select.get("union"), t);
|
||||
}
|
||||
|
||||
if (select.has("column")) {
|
||||
JsonElement a = select.get("column");
|
||||
if (!(a instanceof JsonArray)) {
|
||||
error(path+".column", a, "column is not an array", IssueType.INVALID);
|
||||
} else {
|
||||
content = true;
|
||||
int i = 0;
|
||||
for (JsonElement e : ((JsonArray) a)) {
|
||||
if (!(e instanceof JsonObject)) {
|
||||
error(path+".column["+i+"]", a, "column["+i+"] is a "+e.type().toName()+" not an object", IssueType.INVALID);
|
||||
} else {
|
||||
checkColumn(path+".column["+i+"]", (JsonObject) e, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (select.has("select")) {
|
||||
JsonElement a = select.get("select");
|
||||
if (!(a instanceof JsonArray)) {
|
||||
error(path+".select", a, "select is not an array", IssueType.INVALID);
|
||||
} else {
|
||||
content = true;
|
||||
int i = 0;
|
||||
for (JsonElement e : ((JsonArray) a)) {
|
||||
if (!(e instanceof JsonObject)) {
|
||||
error(path+".select["+i+"]", e, "select["+i+"] is not an object", IssueType.INVALID);
|
||||
} else {
|
||||
checkSelect(path+".select["+i+"]", (JsonObject) e, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
error(path, select, "The select has no columns or selects", IssueType.REQUIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSelectPath(String path, JsonObject select, JsonElement expression, TypeDetails t) {
|
||||
if (!(expression instanceof JsonString)) {
|
||||
error(path+".forEach", expression, "forEach is not a string", IssueType.INVALID);
|
||||
} else {
|
||||
String expr = expression.asString();
|
||||
|
||||
List<String> warnings = new ArrayList<>();
|
||||
TypeDetails td = null;
|
||||
ExpressionNode node = null;
|
||||
try {
|
||||
node = fpe.parse(expr);
|
||||
select.setUserData("path", node);
|
||||
td = fpe.checkOnTypes(null, resourceName, t, node, warnings);
|
||||
} catch (Exception e) {
|
||||
error(path, expression, e.getMessage(), IssueType.INVALID);
|
||||
private boolean checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||
JsonElement a = focus.get("union");
|
||||
if (!(a instanceof JsonArray)) {
|
||||
error(path+".union", a, "union is not an array", IssueType.INVALID);
|
||||
return false;
|
||||
} else {
|
||||
int i = 0;
|
||||
for (JsonElement e : ((JsonArray) a)) {
|
||||
if (!(e instanceof JsonObject)) {
|
||||
error(path+".union["+i+"]", e, "union["+i+"] is not an object", IssueType.INVALID);
|
||||
} else {
|
||||
checkSelect(path+".union["+i+"]", (JsonObject) e, t);
|
||||
}
|
||||
}
|
||||
if (i < 2) {
|
||||
warning(path+".union", a, "union should have more than one item");
|
||||
}
|
||||
if (td != null && node != null) {
|
||||
for (String s : warnings) {
|
||||
warning(path+".path", expression, s);
|
||||
}
|
||||
String columnName = null;
|
||||
JsonElement aliasJ = select.get("alias");
|
||||
if (aliasJ != null) {
|
||||
if (aliasJ instanceof JsonString) {
|
||||
columnName = aliasJ.asString();
|
||||
if (!isValidName(columnName)) {
|
||||
error(path+".name", aliasJ, "The name '"+columnName+"' is not valid", IssueType.VALUE);
|
||||
}
|
||||
} else {
|
||||
error(path+".alias", aliasJ, "alias must be a string", IssueType.INVALID);
|
||||
}
|
||||
}
|
||||
if (columnName == null) {
|
||||
List<String> names = node.getDistalNames();
|
||||
if (names.size() == 1 && names.get(0) != null) {
|
||||
columnName = names.get(0);
|
||||
if (!isValidName(columnName)) {
|
||||
error(path+".path", expression, "The name '"+columnName+"' found in the path expression is not a valid column name, so an alias is required", IssueType.INVARIANT);
|
||||
}
|
||||
} else {
|
||||
error(path, select, "The path does not resolve to a name, so an alias is required", IssueType.REQUIRED);
|
||||
}
|
||||
}
|
||||
// ok, name is sorted!
|
||||
if (columnName != null) {
|
||||
select.setUserData("name", columnName);
|
||||
boolean isColl = (td.getCollectionStatus() != CollectionStatus.SINGLETON) || column(columnName) != null;
|
||||
if (select.has("collection")) {
|
||||
JsonElement collectionJ = select.get("collection");
|
||||
if (!(collectionJ instanceof JsonBoolean)) {
|
||||
error(path+".collection", collectionJ, "collection is not a boolean", IssueType.INVALID);
|
||||
} else {
|
||||
boolean collection = collectionJ.asJsonBoolean().asBoolean();
|
||||
if (!collection && isColl) {
|
||||
isColl = false;
|
||||
warning(path, select, "collection is false, but the path statement(s) might return multiple values for the column '"+columnName+"' some inputs");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isColl && !arrays) {
|
||||
warning(path, expression, "column appears to be a collection, but this is not allowed in this context");
|
||||
}
|
||||
// ok collection is sorted
|
||||
Set<String> types = new HashSet<>();
|
||||
for (String type : td.getTypes()) {
|
||||
types.add(simpleType(type));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkColumn(String path, JsonObject column, TypeDetails t) {
|
||||
if (!column.has("path")) {
|
||||
error(path, column, "no path found", IssueType.INVALID);
|
||||
} else {
|
||||
JsonElement expression = column.get("path");
|
||||
if (!(expression instanceof JsonString)) {
|
||||
error(path+".forEach", expression, "forEach is not a string", IssueType.INVALID);
|
||||
} else {
|
||||
String expr = expression.asString();
|
||||
|
||||
JsonElement typeJ = select.get("type");
|
||||
if (typeJ != null) {
|
||||
if (typeJ instanceof JsonString) {
|
||||
String type = typeJ.asString();
|
||||
if (!td.hasType(type)) {
|
||||
error(path+".type", typeJ, "The path expression does not return a value of the type '"+type, IssueType.VALUE);
|
||||
} else {
|
||||
types.clear();
|
||||
types.add(simpleType(type));
|
||||
List<String> warnings = new ArrayList<>();
|
||||
TypeDetails td = null;
|
||||
ExpressionNode node = null;
|
||||
try {
|
||||
node = fpe.parse(expr);
|
||||
column.setUserData("path", node);
|
||||
td = fpe.checkOnTypes(null, resourceName, t, node, warnings);
|
||||
} catch (Exception e) {
|
||||
error(path, expression, e.getMessage(), IssueType.INVALID);
|
||||
}
|
||||
if (td != null && node != null) {
|
||||
for (String s : warnings) {
|
||||
warning(path+".path", expression, s);
|
||||
}
|
||||
String columnName = null;
|
||||
JsonElement aliasJ = column.get("alias");
|
||||
if (aliasJ != null) {
|
||||
if (aliasJ instanceof JsonString) {
|
||||
columnName = aliasJ.asString();
|
||||
if (!isValidName(columnName)) {
|
||||
error(path+".name", aliasJ, "The name '"+columnName+"' is not valid", IssueType.VALUE);
|
||||
}
|
||||
} else {
|
||||
error(path+".type", typeJ, "type must be a string", IssueType.INVALID);
|
||||
error(path+".alias", aliasJ, "alias must be a string", IssueType.INVALID);
|
||||
}
|
||||
}
|
||||
if (types.size() != 1) {
|
||||
error(path, select, "Unable to determine a type (found "+td.describe()+")", IssueType.BUSINESSRULE);
|
||||
} else {
|
||||
String type = types.iterator().next();
|
||||
if (!isSimpleType(type) && !complexTypes) {
|
||||
error(path, expression, "column is a complex type but this is not allowed in this context", IssueType.BUSINESSRULE);
|
||||
if (columnName == null) {
|
||||
List<String> names = node.getDistalNames();
|
||||
if (names.size() == 1 && names.get(0) != null) {
|
||||
columnName = names.get(0);
|
||||
if (!isValidName(columnName)) {
|
||||
error(path+".path", expression, "The name '"+columnName+"' found in the path expression is not a valid column name, so an alias is required", IssueType.INVARIANT);
|
||||
}
|
||||
} else {
|
||||
Column col = column(columnName);
|
||||
if (col != null) {
|
||||
if (!col.getType().equals(type)) {
|
||||
error(path, expression, "Duplicate definition for "+columnName+" has different types ("+col.getType()+" vs "+type+")", IssueType.BUSINESSRULE);
|
||||
error(path, column, "The path does not resolve to a name, so an alias is required", IssueType.REQUIRED);
|
||||
}
|
||||
}
|
||||
// ok, name is sorted!
|
||||
if (columnName != null) {
|
||||
column.setUserData("name", columnName);
|
||||
boolean isColl = (td.getCollectionStatus() != CollectionStatus.SINGLETON) || column(columnName) != null;
|
||||
if (column.has("collection")) {
|
||||
JsonElement collectionJ = column.get("collection");
|
||||
if (!(collectionJ instanceof JsonBoolean)) {
|
||||
error(path+".collection", collectionJ, "collection is not a boolean", IssueType.INVALID);
|
||||
} else {
|
||||
boolean collection = collectionJ.asJsonBoolean().asBoolean();
|
||||
if (!collection && isColl) {
|
||||
isColl = false;
|
||||
warning(path, column, "collection is false, but the path statement(s) might return multiple values for the column '"+columnName+"' some inputs");
|
||||
}
|
||||
if (col.isColl() != isColl) {
|
||||
error(path, expression, "Duplicate definition for "+columnName+" has different status for collection ("+col.isColl()+" vs "+isColl+")", IssueType.BUSINESSRULE);
|
||||
}
|
||||
}
|
||||
if (isColl) {
|
||||
if (arrays == null) {
|
||||
warning(path, expression, "column appears to be a collection. Collections are not supported in all Runners");
|
||||
} else if (!arrays) {
|
||||
warning(path, expression, "column appears to be a collection, but this is not allowed in this context");
|
||||
}
|
||||
}
|
||||
// ok collection is sorted
|
||||
Set<String> types = new HashSet<>();
|
||||
for (String type : td.getTypes()) {
|
||||
types.add(simpleType(type));
|
||||
}
|
||||
|
||||
JsonElement typeJ = column.get("type");
|
||||
if (typeJ != null) {
|
||||
if (typeJ instanceof JsonString) {
|
||||
String type = typeJ.asString();
|
||||
if (!td.hasType(type)) {
|
||||
error(path+".type", typeJ, "The path expression does not return a value of the type '"+type, IssueType.VALUE);
|
||||
} else {
|
||||
types.clear();
|
||||
types.add(simpleType(type));
|
||||
}
|
||||
} else {
|
||||
columns.add(new Column(columnName, isColl, type, kindForType(type)));
|
||||
error(path+".type", typeJ, "type must be a string", IssueType.INVALID);
|
||||
}
|
||||
}
|
||||
if (types.size() != 1) {
|
||||
error(path, column, "Unable to determine a type (found "+td.describe()+")", IssueType.BUSINESSRULE);
|
||||
} else {
|
||||
String type = types.iterator().next();
|
||||
boolean ok = false;
|
||||
if (!isSimpleType(type)) {
|
||||
if (complexTypes) {
|
||||
warning(path, expression, "Column is a complex type. This is not supported in some Runners");
|
||||
} else if (!complexTypes) {
|
||||
error(path, expression, "Column is a complex type but this is not allowed in this context", IssueType.BUSINESSRULE);
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
Column col = column(columnName);
|
||||
if (col != null) {
|
||||
if (!col.getType().equals(type)) {
|
||||
error(path, expression, "Duplicate definition for "+columnName+" has different types ("+col.getType()+" vs "+type+")", IssueType.BUSINESSRULE);
|
||||
}
|
||||
if (col.isColl() != isColl) {
|
||||
error(path, expression, "Duplicate definition for "+columnName+" has different status for collection ("+col.isColl()+" vs "+isColl+")", IssueType.BUSINESSRULE);
|
||||
}
|
||||
} else {
|
||||
columns.add(new Column(columnName, isColl, type, kindForType(type)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -295,9 +380,10 @@ public class Validator {
|
|||
return type;
|
||||
}
|
||||
|
||||
private void checkForEach(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||
private TypeDetails checkForEach(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||
if (!(expression instanceof JsonString)) {
|
||||
error(path+".forEach", expression, "forEach is not a string", IssueType.INVALID);
|
||||
return null;
|
||||
} else {
|
||||
String expr = expression.asString();
|
||||
|
||||
|
@ -312,28 +398,36 @@ public class Validator {
|
|||
}
|
||||
if (td != null) {
|
||||
for (String s : warnings) {
|
||||
warning(path+".path", expression, s);
|
||||
}
|
||||
int i = 0;
|
||||
if (checkAllObjects(path, focus, "select")) {
|
||||
for (JsonObject select : focus.getJsonObjects("select")) {
|
||||
checkSelect(path+".select["+i+"]", select, td);
|
||||
i++;
|
||||
}
|
||||
if (i == 0) {
|
||||
error(path, focus, "No select statements found", IssueType.REQUIRED);
|
||||
}
|
||||
warning(path+".forEach", expression, s);
|
||||
}
|
||||
}
|
||||
return td;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForEachOrNull(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||
error(path+".forEachOrNull", expression, "forEachOrNull is not supported", IssueType.BUSINESSRULE);
|
||||
}
|
||||
private TypeDetails checkForEachOrNull(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||
if (!(expression instanceof JsonString)) {
|
||||
error(path+".forEachOrNull", expression, "forEachOrNull is not a string", IssueType.INVALID);
|
||||
return null;
|
||||
} else {
|
||||
String expr = expression.asString();
|
||||
|
||||
private void checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||
error(path+".union", focus.get("union"), "union is not supported", IssueType.BUSINESSRULE);
|
||||
List<String> warnings = new ArrayList<>();
|
||||
TypeDetails td = null;
|
||||
try {
|
||||
ExpressionNode n = fpe.parse(expr);
|
||||
focus.setUserData("forEachOrNull", n);
|
||||
td = fpe.checkOnTypes(null, resourceName, t, n, warnings);
|
||||
} catch (Exception e) {
|
||||
error(path, expression, e.getMessage(), IssueType.INVALID);
|
||||
}
|
||||
if (td != null) {
|
||||
for (String s : warnings) {
|
||||
warning(path+".forEachOrNull", expression, s);
|
||||
}
|
||||
}
|
||||
return td;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConstant(String path, JsonObject constant) {
|
||||
|
@ -481,27 +575,40 @@ public class Validator {
|
|||
issues.add(vm);
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
for (ValidationMessage vm : issues) {
|
||||
System.out.println(vm.summary());
|
||||
}
|
||||
|
||||
private void hint(String path, JsonElement e, String issue) {
|
||||
ValidationMessage vm = new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, e.getStart().getLine(), e.getStart().getCol(), path, issue, IssueSeverity.INFORMATION);
|
||||
issues.add(vm);
|
||||
}
|
||||
|
||||
public void check() {
|
||||
public void dump() {
|
||||
for (ValidationMessage vm : issues) {
|
||||
System.out.println(vm.summary());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void check() {
|
||||
if (!isOk()) {
|
||||
throw new FHIRException("View Definition is not valid");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<ValidationMessage> getIssues() {
|
||||
return issues;
|
||||
}
|
||||
|
||||
public boolean isOk() {
|
||||
boolean ok = true;
|
||||
for (ValidationMessage vm : issues) {
|
||||
if (vm.isError()) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
throw new FHIRException("View Definition is not valid");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,6 +337,11 @@ public class ValidatorCli {
|
|||
res.add("5.0");
|
||||
res.add("-ig");
|
||||
res.add("hl7.cda.uv.core#2.1.0-draft1");
|
||||
} else if (a.equals("-view-definition")) {
|
||||
res.add("-version");
|
||||
res.add("5.0");
|
||||
res.add("-ig");
|
||||
res.add("hl7.fhir.uv.sql-on-fhir#current");
|
||||
} else {
|
||||
res.add(a);
|
||||
}
|
||||
|
|
|
@ -160,6 +160,7 @@ import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.TypedElementDefinition;
|
|||
import org.hl7.fhir.r5.utils.ResourceUtilities;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.r5.utils.sql.Validator;
|
||||
import org.hl7.fhir.r5.utils.validation.BundleValidationRule;
|
||||
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
|
||||
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
||||
|
@ -429,6 +430,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return context.fetchResource(ValueSet.class, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean paramIsType(String name, int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
private FHIRPathEngine fpe;
|
||||
|
||||
|
@ -5379,6 +5385,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return new StructureMapValidator(this, fpe, profileUtilities).validateStructureMap(errors, element, stack) && ok;
|
||||
} else if (element.getType().equals("ValueSet")) {
|
||||
return new ValueSetValidator(this).validateValueSet(errors, element, stack) && ok;
|
||||
} else if ("http://hl7.org/fhir/uv/sql-on-fhir/StructureDefinition/ViewDefinition".equals(element.getProperty().getStructure().getUrl())) {
|
||||
if (element.getNativeObject() != null && element.getNativeObject() instanceof JsonObject) {
|
||||
JsonObject json = (JsonObject) element.getNativeObject();
|
||||
Validator sqlv = new Validator(context, fpe, new ArrayList<>(), null, null, null);
|
||||
sqlv.checkViewDefinition(stack.getLiteralPath(), json);
|
||||
errors.addAll(sqlv.getIssues());
|
||||
ok = sqlv.isOk() && ok;
|
||||
}
|
||||
return ok;
|
||||
} else {
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -397,6 +397,10 @@ public class SnapShotGenerationXTests {
|
|||
throw new Error("Not implemented yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean paramIsType(String name, int index) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static FHIRPathEngine fp;
|
||||
|
@ -577,4 +581,6 @@ public class SnapShotGenerationXTests {
|
|||
}
|
||||
return sd;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -804,4 +804,9 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean paramIsType(String name, int index) {
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue