Reconcile R4/R5 implementations

This commit is contained in:
Grahame Grieve 2019-08-23 15:38:58 +10:00
parent 07f4743cd1
commit 512c3932a3
35 changed files with 2270 additions and 1332 deletions

View File

@ -891,7 +891,7 @@ public class ProfileComparer {
if (nw.hasAggregation())
throw new DefinitionException("Aggregation not supported: "+path);
for (TypeRefComponent ex : results) {
if (Utilities.equals(ex.getCode(), nw.getCode())) {
if (Utilities.equals(ex.getWorkingCode(), nw.getWorkingCode())) {
if (!ex.hasProfile() && !nw.hasProfile())
pfound = true;
else if (!ex.hasProfile()) {
@ -1070,7 +1070,7 @@ public class ProfileComparer {
private String typeCode(DefinitionNavigator defn) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (TypeRefComponent t : defn.current().getType())
b.append(t.getCode()+(t.hasProfile() ? "("+t.getProfile()+")" : "")+(t.hasTargetProfile() ? "("+t.getTargetProfile()+")" : "")); // todo: other properties
b.append(t.getWorkingCode()+(t.hasProfile() ? "("+t.getProfile()+")" : "")+(t.hasTargetProfile() ? "("+t.getTargetProfile()+")" : "")); // todo: other properties
return b.toString();
}

View File

@ -264,14 +264,15 @@ public class ProfileUtilities extends TranslatingUtilities {
public String display;
public String url;
}
boolean isDatatype(String typeSimple);
boolean isResource(String typeSimple);
boolean hasLinkFor(String typeSimple);
String getLinkFor(String corePath, String typeSimple);
BindingResolution resolveBinding(StructureDefinition def, ElementDefinitionBindingComponent binding, String path) throws FHIRException;
BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException;
String getLinkForProfile(StructureDefinition profile, String url);
boolean prependLinks();
public boolean isDatatype(String typeSimple);
public boolean isResource(String typeSimple);
public boolean hasLinkFor(String typeSimple);
public String getLinkFor(String corePath, String typeSimple);
public BindingResolution resolveBinding(StructureDefinition def, ElementDefinitionBindingComponent binding, String path) throws FHIRException;
public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException;
public String getLinkForProfile(StructureDefinition profile, String url);
public boolean prependLinks();
public String getLinkForUrl(String corePath, String s);
}
@ -448,7 +449,7 @@ public class ProfileUtilities extends TranslatingUtilities {
e.clearUserData(GENERATED_IN_SNAPSHOT);
// we actually delegate the work to a subroutine so we can re-enter it with a different cursors
StructureDefinitionDifferentialComponent diff = derived.getDifferential().copy(); // we make a copy here because we're sometimes going to hack the differential while processing it.
StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards
processPaths("", derived.getSnapshot(), base.getSnapshot(), diff, baseCursor, diffCursor, base.getSnapshot().getElement().size()-1,
derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, new ArrayList<ElementRedirection>(), base);
@ -467,6 +468,14 @@ public class ProfileUtilities extends TranslatingUtilities {
setIds(derived, false);
//Check that all differential elements have a corresponding snapshot element
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData("diff-source"))
throw new Error("Unxpected internal condition - no source on diff element");
else {
if (e.hasUserData(DERIVATION_EQUALS))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS));
if (e.hasUserData(DERIVATION_POINTER))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER));
}
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
System.out.println("Error in snapshot generation: Differential for "+derived.getUrl()+" with " + (e.hasId() ? "id: "+e.getId() : "path: "+e.getPath())+" has an element that is not marked with a snapshot match");
if (exception)
@ -489,6 +498,17 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
private StructureDefinitionDifferentialComponent cloneDiff(StructureDefinitionDifferentialComponent source) {
StructureDefinitionDifferentialComponent diff = new StructureDefinitionDifferentialComponent();
for (ElementDefinition sed : source.getElement()) {
ElementDefinition ted = sed.copy();
diff.getElement().add(ted);
ted.setUserData("diff-source", sed);
}
return diff;
}
private String constraintSummary(ElementDefinition ed) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
if (ed.hasPattern())
@ -528,7 +548,7 @@ public class ProfileUtilities extends TranslatingUtilities {
first = false;
else
b.append("|");
b.append(tr.getCode());
b.append(tr.getWorkingCode());
}
return b.toString();
}
@ -541,7 +561,7 @@ public class ProfileUtilities extends TranslatingUtilities {
first = false;
else
b.append("|");
b.append(tr.getCode());
b.append(tr.getWorkingCode());
if (tr.hasProfile()) {
b.append("(");
b.append(tr.getProfile());
@ -613,7 +633,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
if (outcome.getType().size() > 1) {
for (TypeRefComponent t : outcome.getType()) {
if (!t.getCode().equals("Reference"))
if (!t.getWorkingCode().equals("Reference"))
throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") and multiple types ("+typeCode(outcome.getType())+") in profile "+profileName);
}
}
@ -631,7 +651,7 @@ public class ProfileUtilities extends TranslatingUtilities {
baseCursor++;
} else if (diffMatches.size() == 1 && (slicingDone || (!isImplicitSlicing(diffMatches.get(0), cpath) && !(diffMatches.get(0).hasSlicing() || (isExtension(diffMatches.get(0)) && diffMatches.get(0).hasSliceName()))))) {// one matching element in the differential
ElementDefinition template = null;
if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getCode())) {
if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
if (sd != null) {
@ -1182,10 +1202,12 @@ public class ProfileUtilities extends TranslatingUtilities {
String s = n.substring(rn.length());
if (!s.contains(".")) {
if (ed.hasSliceName() && ed.getType().size() == 1) {
typeList.add(new TypeSlice(ed, ed.getTypeFirstRep().getCode()));
typeList.add(new TypeSlice(ed, ed.getTypeFirstRep().getWorkingCode()));
} else if (!ed.hasSliceName() && !s.equals("[x]")) {
if (isDataType(s))
typeList.add(new TypeSlice(ed, s));
else if (isConstrainedDataType(s))
typeList.add(new TypeSlice(ed, baseType(s)));
else if (isPrimitive(Utilities.uncapitalize(s)))
typeList.add(new TypeSlice(ed, Utilities.uncapitalize(s)));
} else if (!ed.hasSliceName() && s.equals("[x]"))
@ -1207,7 +1229,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private List<TypeRefComponent> getByTypeName(List<TypeRefComponent> type, String t) {
List<TypeRefComponent> res = new ArrayList<TypeRefComponent>();
for (TypeRefComponent tr : type) {
if (t.equals(tr.getCode()))
if (t.equals(tr.getWorkingCode()))
res.add(tr);
}
return res;
@ -1401,9 +1423,9 @@ public class ProfileUtilities extends TranslatingUtilities {
System.out.println("Failed to find referenced profile: " + type.getProfile());
}
if (sd == null)
sd = context.fetchTypeDefinition(type.getCode());
sd = context.fetchTypeDefinition(type.getWorkingCode());
if (sd == null)
System.out.println("XX: failed to find profle for type: " + type.getCode()); // debug GJM
System.out.println("XX: failed to find profle for type: " + type.getWorkingCode()); // debug GJM
return sd;
}
@ -1420,7 +1442,7 @@ public class ProfileUtilities extends TranslatingUtilities {
boolean first = true;
for (TypeRefComponent type : types) {
if (first) first = false; else b.append(", ");
b.append(type.getCode());
b.append(type.getWorkingCode());
if (type.hasTargetProfile())
b.append("{"+type.getTargetProfile()+"}");
else if (type.hasProfile())
@ -1434,7 +1456,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (types.isEmpty())
return false;
for (TypeRefComponent type : types) {
String t = type.getCode();
String t = type.getWorkingCode();
if (!isDataType(t) && !isPrimitive(t))
return false;
}
@ -1894,15 +1916,11 @@ public class ProfileUtilities extends TranslatingUtilities {
// }
boolean ok = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
String t = ts.getCode();
if (t == null && ts.getCodeElement().hasExtension(ToolingExtensions.EXT_XML_TYPE))
t = "*"; //
String t = ts.getWorkingCode();
for (TypeRefComponent td : base.getType()) {;
String tt = td.getCode();
if (tt == null && td.getCodeElement().hasExtension(ToolingExtensions.EXT_JSON_TYPE))
tt = "*"; //
String tt = td.getWorkingCode();
b.append(tt);
if (td.hasCode() && (tt.equals(t) || "Extension".equals(tt) ||
if (td.hasCode() && (tt.equals(t) || "Extension".equals(tt) || (t.equals("uri") && tt.equals("string")) || // work around for old badly generated SDs
"Element".equals(tt) || "*".equals(tt) ||
(("Resource".equals(tt) || ("DomainResource".equals(tt)) && pkp.isResource(t)))))
ok = true;
@ -1973,7 +1991,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private boolean hasBindableType(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
if (Utilities.existsInList(tr.getCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code"))
if (Utilities.existsInList(tr.getWorkingCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code"))
return true;
}
return false;
@ -2255,7 +2273,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null)));
tl = t;
if (t.hasTarget()) {
c.getPieces().add(gen.new Piece(corePath+"references.html", t.getCode(), null));
c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null));
c.getPieces().add(gen.new Piece(null, "(", null));
boolean tfirst = true;
for (UriType u : t.getTargetProfile()) {
@ -2263,27 +2281,7 @@ public class ProfileUtilities extends TranslatingUtilities {
tfirst = false;
else
c.addPiece(gen.new Piece(null, " | ", null));
if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
if (sd != null) {
String disp = sd.hasTitle() ? sd.getTitle() : sd.getName();
c.addPiece(checkForNoChange(t, gen.new Piece(checkPrepend(corePath, sd.getUserString("path")), disp, null)));
} else {
String rn = u.getValue().substring(40);
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, rn), rn, null)));
}
} else if (Utilities.isAbsoluteUrl(u.getValue())) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
if (sd != null) {
String disp = sd.hasTitle() ? sd.getTitle() : sd.getName();
String ref = pkp.getLinkForProfile(null, sd.getUrl());
if (ref.contains("|"))
ref = ref.substring(0, ref.indexOf("|"));
c.addPiece(checkForNoChange(t, gen.new Piece(ref, disp, null)));
} else
c.addPiece(checkForNoChange(t, gen.new Piece(null, u.getValue(), null)));
} else if (t.hasTargetProfile() && u.getValue().startsWith("#"))
c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+u.getValue().substring(1).toLowerCase()+".html", u.getValue(), null)));
genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue());
}
c.getPieces().add(gen.new Piece(null, ")", null));
if (t.getAggregation().size() > 0) {
@ -2298,24 +2296,22 @@ public class ProfileUtilities extends TranslatingUtilities {
}
c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null));
}
} else if (t.hasProfile() && (!t.getCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type
} else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type
String ref;
ref = pkp.getLinkForProfile(profile, t.getProfile().get(0).getValue());
if (ref != null) {
String[] parts = ref.split("\\|");
if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) {
// c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], "<" + parts[1] + ">", t.getCode()))); Lloyd
c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getCode())));
c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode())));
} else {
// c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().startsWith(corePath)? corePath: "")+parts[0], "<" + parts[1] + ">", t.getCode())));
c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().get(0).getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getCode())));
c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().get(0).getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode())));
}
} else
c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().get(0).getValue().startsWith(corePath)? corePath: "")+ref, t.getCode(), null)));
c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().get(0).getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null)));
} else {
String tc = t.getCode();
if (Utilities.noString(tc) && t.getCodeElement().hasExtension(ToolingExtensions.EXT_JSON_TYPE))
tc = "string";
String tc = t.getWorkingCode();
if (pkp != null && pkp.hasLinkFor(tc)) {
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null)));
} else
@ -2325,6 +2321,31 @@ public class ProfileUtilities extends TranslatingUtilities {
return c;
}
public void genTargetLink(HierarchicalTableGenerator gen, String profileBaseFileName, String corePath, Cell c, TypeRefComponent t, String u) {
if (u.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u);
if (sd != null) {
String disp = sd.hasTitle() ? sd.getTitle() : sd.getName();
c.addPiece(checkForNoChange(t, gen.new Piece(checkPrepend(corePath, sd.getUserString("path")), disp, null)));
} else {
String rn = u.substring(40);
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, rn), rn, null)));
}
} else if (Utilities.isAbsoluteUrl(u)) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u);
if (sd != null) {
String disp = sd.hasTitle() ? sd.getTitle() : sd.getName();
String ref = pkp.getLinkForProfile(null, sd.getUrl());
if (ref.contains("|"))
ref = ref.substring(0, ref.indexOf("|"));
c.addPiece(checkForNoChange(t, gen.new Piece(ref, disp, null)));
} else
c.addPiece(checkForNoChange(t, gen.new Piece(null, u, null)));
} else if (t.hasTargetProfile() && u.startsWith("#"))
c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+u.substring(1).toLowerCase()+".html", u, null)));
}
private boolean isProfiledType(List<CanonicalType> theProfile) {
for (CanonicalType next : theProfile){
if (StringUtils.defaultString(next.getValueAsString()).contains(":")) {
@ -2473,8 +2494,11 @@ public class ProfileUtilities extends TranslatingUtilities {
List<ElementDefinition> list = diff ? profile.getDifferential().getElement() : profile.getSnapshot().getElement();
List<StructureDefinition> profiles = new ArrayList<StructureDefinition>();
profiles.add(profile);
if (list.isEmpty())
throw new FHIRException((diff ? "Differential" : "Snapshot") + " is empty generating hierarchical table for "+profile.getUrl());
if (list.isEmpty()) {
ElementDefinition root = new ElementDefinition().setPath(profile.getType());
root.setId(profile.getType());
list.add(root);
}
genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null);
try {
return gen.generate(model, imagePath, 0, outputTracker);
@ -2513,7 +2537,7 @@ public class ProfileUtilities extends TranslatingUtilities {
StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1);
String s = tail(element.getPath());
if (element.hasSliceName())
s = element.getSliceName();
s = s +":"+element.getSliceName();
Row typesRow = null;
List<ElementDefinition> children = getChildren(all, element);
@ -2533,13 +2557,13 @@ public class ProfileUtilities extends TranslatingUtilities {
row.setLineColor(0);
boolean hasDef = element != null;
boolean ext = false;
if (s.equals("extension")) {
if (tail(element.getPath()).equals("extension")) {
if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue()))
row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX);
else
row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE);
ext = true;
} else if (s.equals("modifierExtension")) {
} else if (tail(element.getPath()).equals("modifierExtension")) {
if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue()))
row.setIcon("icon_modifier_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX);
else
@ -2553,13 +2577,13 @@ public class ProfileUtilities extends TranslatingUtilities {
row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE);
typesRow = row;
}
} else if (hasDef && element.getType().get(0).getCode() != null && element.getType().get(0).getCode().startsWith("@"))
} else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@"))
row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE);
else if (hasDef && isPrimitive(element.getType().get(0).getCode()))
else if (hasDef && isPrimitive(element.getType().get(0).getWorkingCode()))
row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE);
else if (hasDef && element.getType().get(0).hasTarget())
row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE);
else if (hasDef && isDataType(element.getType().get(0).getCode()))
else if (hasDef && isDataType(element.getType().get(0).getWorkingCode()))
row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE);
else
row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE);
@ -2629,12 +2653,13 @@ public class ProfileUtilities extends TranslatingUtilities {
} else {
row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE);
slicingRow = row;
row.getCells().get(2).getPieces().clear();
for (Cell cell : row.getCells())
for (Piece p : cell.getPieces()) {
p.addStyle("font-style: italic");
}
}
} else if (element.hasSliceName()) {
row.setIcon("icon_slice_item.png", HierarchicalTableGenerator.TEXT_ICON_SLICE_ITEM);
}
if (used.used || showMissing)
rows.add(row);
@ -2689,17 +2714,17 @@ public class ProfileUtilities extends TranslatingUtilities {
// genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants);
}
if (typesRow != null) {
makeChoiceRows(typesRow.getSubRows(), element, gen, corePath);
makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName);
}
}
return slicingRow;
}
private void makeChoiceRows(List<Row> subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath) {
private void makeChoiceRows(List<Row> subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName) {
// create a child for each choice
for (TypeRefComponent tr : element.getType()) {
Row choicerow = gen.new Row();
String t = tr.getCode();
String t = tr.getWorkingCode();
if (isReference(t)) {
choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null));
choicerow.getCells().add(gen.new Cell());
@ -2708,7 +2733,7 @@ public class ProfileUtilities extends TranslatingUtilities {
Cell c = gen.new Cell();
choicerow.getCells().add(c);
if (ADD_REFERENCE_TO_TABLE) {
if (tr.getCode().equals("canonical"))
if (tr.getWorkingCode().equals("canonical"))
c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null));
else
c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null));
@ -2718,7 +2743,7 @@ public class ProfileUtilities extends TranslatingUtilities {
for (CanonicalType rt : tr.getTargetProfile()) {
if (!first)
c.getPieces().add(gen.new Piece(null, " | ", null));
c.getPieces().add(gen.new Piece(null, pkp.getLinkFor(corePath, rt.asStringValue()), null));
genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue());
first = false;
}
if (ADD_REFERENCE_TO_TABLE)
@ -3034,9 +3059,13 @@ public class ProfileUtilities extends TranslatingUtilities {
if (definition.hasFixed()) {
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold")));
if (!useTableForFixedValues || definition.getFixed().isPrimitive())
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, buildJson(definition.getFixed()), null).addStyle("color: darkgreen")));
else {
if (!useTableForFixedValues || definition.getFixed().isPrimitive()) {
String s = buildJson(definition.getFixed());
String link = null;
if (Utilities.isAbsoluteUrl(s))
link = pkp.getLinkForUrl(corePath, s);
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
} else {
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen")));
genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath);
}
@ -3158,9 +3187,11 @@ public class ProfileUtilities extends TranslatingUtilities {
c.addPiece(gen.new Piece("br"));
c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold"));
String s = b.primitiveValue();
if (Utilities.noString(s))
System.out.print("t");
c.getPieces().add(gen.new Piece(null, s, null).addStyle("color: darkgreen"));
// ok. let's see if we can find a relevant link for this
String link = null;
if (Utilities.isAbsoluteUrl(s))
link = pkp.getLinkForUrl(corePath, s);
c.getPieces().add(gen.new Piece(link, s, null).addStyle("color: darkgreen"));
} else {
c = gen.new Cell();
row.getCells().add(c);
@ -3297,7 +3328,11 @@ public class ProfileUtilities extends TranslatingUtilities {
if (definition.hasFixed()) {
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, buildJson(definition.getFixed()), null).addStyle("color: darkgreen")));
String s = buildJson(definition.getFixed());
String link = null;
if (Utilities.isAbsoluteUrl(s))
link = pkp.getLinkForUrl(corePath, s);
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
} else if (definition.hasPattern()) {
if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br"));
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold")));
@ -3427,13 +3462,37 @@ public class ProfileUtilities extends TranslatingUtilities {
private boolean isDataType(String value) {
StructureDefinition sd = context.fetchTypeDefinition(value);
return sd != null && sd.getKind() == StructureDefinitionKind.COMPLEXTYPE;
if (sd == null) // might be running before all SDs are available
return Utilities.existsInList(value, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing",
"ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext");
else
return sd.getKind() == StructureDefinitionKind.COMPLEXTYPE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION;
}
private boolean isConstrainedDataType(String value) {
StructureDefinition sd = context.fetchTypeDefinition(value);
if (sd == null) // might be running before all SDs are available
return Utilities.existsInList(value, "SimpleQuantity", "MoneyQuantity");
else
return sd.getKind() == StructureDefinitionKind.COMPLEXTYPE && sd.getDerivation() == TypeDerivationRule.CONSTRAINT;
}
private String baseType(String value) {
StructureDefinition sd = context.fetchTypeDefinition(value);
if (sd != null) // might be running before all SDs are available
return sd.getType();
if (Utilities.existsInList(value, "SimpleQuantity", "MoneyQuantity"))
return "Quantity";
throw new Error("Internal error - type not known "+value);
}
public boolean isPrimitive(String value) {
StructureDefinition sd = context.fetchTypeDefinition(value);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
if (sd == null) // might be running before all SDs are available
return Utilities.existsInList(value, "base64Binary", "boolean", "canonical", "code", "date", "dateTime", "decimal", "id", "instant", "integer", "markdown", "oid", "positiveInt", "string", "time", "unsignedInt", "uri", "url", "uuid");
else
return sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
// private static String listStructures(StructureDefinition p) {
@ -3692,24 +3751,24 @@ public class ProfileUtilities extends TranslatingUtilities {
// what we have to check for here is running off the base profile into a data type profile
ElementDefinition ed = cmp.snapshot.get(child.getBaseIndex());
ElementDefinitionComparer ccmp;
if (ed.getType().isEmpty() || isAbstract(ed.getType().get(0).getCode()) || ed.getType().get(0).getCode().equals(ed.getPath())) {
if (ed.getType().isEmpty() || isAbstract(ed.getType().get(0).getWorkingCode()) || ed.getType().get(0).getWorkingCode().equals(ed.getPath())) {
ccmp = new ElementDefinitionComparer(true, cmp.snapshot, cmp.base, cmp.prefixLength, cmp.name);
} else if (ed.getType().get(0).getCode().equals("Extension") && child.getSelf().getType().size() == 1 && child.getSelf().getType().get(0).hasProfile()) {
} else if (ed.getType().get(0).getWorkingCode().equals("Extension") && child.getSelf().getType().size() == 1 && child.getSelf().getType().get(0).hasProfile()) {
StructureDefinition profile = context.fetchResource(StructureDefinition.class, child.getSelf().getType().get(0).getProfile().get(0).getValue());
if (profile==null)
ccmp = null; // this might happen before everything is loaded. And we don't so much care about sot order in this case
else
ccmp = new ElementDefinitionComparer(true, profile.getSnapshot().getElement(), ed.getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name);
} else if (ed.getType().size() == 1 && !ed.getType().get(0).getCode().equals("*")) {
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(ed.getType().get(0).getCode()));
ccmp = new ElementDefinitionComparer(true, profile.getSnapshot().getElement(), ed.getType().get(0).getWorkingCode(), child.getSelf().getPath().length(), cmp.name);
} else if (ed.getType().size() == 1 && !ed.getType().get(0).getWorkingCode().equals("*")) {
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(ed.getType().get(0).getWorkingCode()));
if (profile==null)
throw new FHIRException("Unable to resolve profile " + sdNs(ed.getType().get(0).getCode()) + " in element " + ed.getPath());
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), ed.getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name);
throw new FHIRException("Unable to resolve profile " + sdNs(ed.getType().get(0).getWorkingCode()) + " in element " + ed.getPath());
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), ed.getType().get(0).getWorkingCode(), child.getSelf().getPath().length(), cmp.name);
} else if (child.getSelf().getType().size() == 1) {
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(child.getSelf().getType().get(0).getCode()));
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(child.getSelf().getType().get(0).getWorkingCode()));
if (profile==null)
throw new FHIRException("Unable to resolve profile " + sdNs(ed.getType().get(0).getCode()) + " in element " + ed.getPath());
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), child.getSelf().getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name);
throw new FHIRException("Unable to resolve profile " + sdNs(ed.getType().get(0).getWorkingCode()) + " in element " + ed.getPath());
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), child.getSelf().getType().get(0).getWorkingCode(), child.getSelf().getPath().length(), cmp.name);
} else if (ed.getPath().endsWith("[x]") && !child.getSelf().getPath().endsWith("[x]")) {
String edLastNode = ed.getPath().replaceAll("(.*\\.)*(.*)", "$2");
String childLastNode = child.getSelf().getPath().replaceAll("(.*\\.)*(.*)", "$2");
@ -3720,27 +3779,27 @@ public class ProfileUtilities extends TranslatingUtilities {
if (sd == null)
throw new Error("Unable to find profile '"+p+"' at "+ed.getId());
ccmp = new ElementDefinitionComparer(false, sd.getSnapshot().getElement(), p, child.getSelf().getPath().length(), cmp.name);
} else if (child.getSelf().hasType() && child.getSelf().getType().get(0).getCode().equals("Reference")) {
} else if (child.getSelf().hasType() && child.getSelf().getType().get(0).getWorkingCode().equals("Reference")) {
for (TypeRefComponent t: child.getSelf().getType()) {
if (!t.getCode().equals("Reference")) {
if (!t.getWorkingCode().equals("Reference")) {
throw new Error("Can't have children on an element with a polymorphic type - you must slice and constrain the types first (sortElements: "+ed.getPath()+":"+typeCode(ed.getType())+")");
}
}
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(ed.getType().get(0).getCode()));
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), ed.getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name);
} else if (!child.getSelf().hasType() && ed.getType().get(0).getCode().equals("Reference")) {
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(ed.getType().get(0).getWorkingCode()));
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), ed.getType().get(0).getWorkingCode(), child.getSelf().getPath().length(), cmp.name);
} else if (!child.getSelf().hasType() && ed.getType().get(0).getWorkingCode().equals("Reference")) {
for (TypeRefComponent t: ed.getType()) {
if (!t.getCode().equals("Reference")) {
if (!t.getWorkingCode().equals("Reference")) {
throw new Error("Not handled yet (sortElements: "+ed.getPath()+":"+typeCode(ed.getType())+")");
}
}
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(ed.getType().get(0).getCode()));
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), ed.getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name);
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs(ed.getType().get(0).getWorkingCode()));
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), ed.getType().get(0).getWorkingCode(), child.getSelf().getPath().length(), cmp.name);
} else {
// this is allowed if we only profile the extensions
StructureDefinition profile = context.fetchResource(StructureDefinition.class, sdNs("Element"));
if (profile==null)
throw new FHIRException("Unable to resolve profile " + sdNs(ed.getType().get(0).getCode()) + " in element " + ed.getPath());
throw new FHIRException("Unable to resolve profile " + sdNs(ed.getType().get(0).getWorkingCode()) + " in element " + ed.getPath());
ccmp = new ElementDefinitionComparer(false, profile.getSnapshot().getElement(), "Element", child.getSelf().getPath().length(), cmp.name);
// throw new Error("Not handled yet (sortElements: "+ed.getPath()+":"+typeCode(ed.getType())+")");
}
@ -3842,7 +3901,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!structure.hasSnapshot())
throw new DefinitionException("needs a snapshot");
XLSXWriter xlsx = new XLSXWriter(dest, structure, asXml);
XLSXWriter xlsx = new XLSXWriter(dest, structure, asXml, hideMustSupportFalse);
for (ElementDefinition child : structure.getSnapshot().getElement()) {
xlsx.processElement(child);

View File

@ -34,6 +34,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.model.Constants;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.Enumerations;
@ -568,7 +569,7 @@ public class ShExGenerator {
private String genTypeRef(StructureDefinition sd, ElementDefinition ed, String id, ElementDefinition.TypeRefComponent typ) {
if(typ.hasProfile()) {
if(typ.getCode().equals("Reference"))
if(typ.getWorkingCode().equals("Reference"))
return genReference("", typ);
else if(ProfileUtilities.getChildList(sd, ed).size() > 0) {
// inline anonymous type - give it a name and factor it out
@ -581,13 +582,8 @@ public class ShExGenerator {
return simpleElement(sd, ed, ref);
}
} else if (typ.getCodeElement().getExtensionsByUrl(ToolingExtensions.EXT_RDF_TYPE).size() > 0) {
String xt = null;
try {
xt = typ.getCodeElement().getExtensionString(ToolingExtensions.EXT_RDF_TYPE);
} catch (FHIRException e) {
e.printStackTrace();
}
} else if (typ.getWorkingCode().startsWith(Constants.NS_SYSTEM_TYPE)) {
String xt = typ.getWorkingCode();
// TODO: Remove the next line when the type of token gets switched to string
// TODO: Add a rdf-type entry for valueInteger to xsd:integer (instead of int)
ST td_entry = tmplt(PRIMITIVE_ELEMENT_DEFN_TEMPLATE).add("typ",
@ -612,16 +608,16 @@ public class ShExGenerator {
td_entry.add("facets", facets.toString());
return td_entry.render();
} else if (typ.getCode() == null) {
} else if (typ.getWorkingCode() == null) {
ST primitive_entry = tmplt(PRIMITIVE_ELEMENT_DEFN_TEMPLATE);
primitive_entry.add("typ", "xsd:string");
return primitive_entry.render();
} else if(typ.getCode().equals("xhtml")) {
} else if(typ.getWorkingCode().equals("xhtml")) {
return tmplt(XHTML_TYPE_TEMPLATE).render();
} else {
datatypes.add(typ.getCode());
return simpleElement(sd, ed, typ.getCode());
datatypes.add(typ.getWorkingCode());
return simpleElement(sd, ed, typ.getWorkingCode());
}
}
@ -653,8 +649,8 @@ public class ShExGenerator {
* @return ShEx equivalent
*/
private String genAltEntry(String id, ElementDefinition.TypeRefComponent typ) {
if(!typ.getCode().equals("Reference"))
throw new AssertionError("We do not handle " + typ.getCode() + " alternatives");
if(!typ.getWorkingCode().equals("Reference"))
throw new AssertionError("We do not handle " + typ.getWorkingCode() + " alternatives");
return genReference(id, typ);
}
@ -685,7 +681,7 @@ public class ShExGenerator {
private String genChoiceEntry(StructureDefinition sd, ElementDefinition ed, String id, String base, ElementDefinition.TypeRefComponent typ) {
ST shex_choice_entry = tmplt(ELEMENT_TEMPLATE);
String ext = typ.getCode();
String ext = typ.getWorkingCode();
shex_choice_entry.add("id", "fhir:" + base+Character.toUpperCase(ext.charAt(0)) + ext.substring(1) + " ");
shex_choice_entry.add("card", "");
shex_choice_entry.add("defn", genTypeRef(sd, ed, id, typ));
@ -745,7 +741,7 @@ public class ShExGenerator {
String[] els = typ.getProfile().get(0).getValue().split("/");
return els[els.length - 1];
} else {
return typ.getCode();
return typ.getWorkingCode();
}
}

View File

@ -355,7 +355,7 @@ public class XmlSchemaGenerator {
throw new Error("Common ancester not found at "+edc.getPath());
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (TypeRefComponent t : edc.getType()) {
b.append(getQN(sd, edc, t.getCode(), true).toString());
b.append(getQN(sd, edc, t.getWorkingCode(), true).toString());
}
String name = tailDot(edc.getPath());
@ -376,8 +376,8 @@ public class XmlSchemaGenerator {
} else for (TypeRefComponent t : edc.getType()) {
String name = tailDot(edc.getPath());
if (edc.getType().size() > 1)
name = name + Utilities.capitalize(t.getCode());
QName qn = getQN(sd, edc, t.getCode(), true);
name = name + Utilities.capitalize(t.getWorkingCode());
QName qn = getQN(sd, edc, t.getWorkingCode(), true);
String min = String.valueOf(edc.getMin());
String max = edc.getMax();
if ("*".equals(max))
@ -436,13 +436,13 @@ public class XmlSchemaGenerator {
}
private StructureDefinition getCommonAncestor(List<TypeRefComponent> type) throws FHIRException {
StructureDefinition sd = library.get(type.get(0).getCode());
StructureDefinition sd = library.get(type.get(0).getWorkingCode());
if (sd == null)
throw new FHIRException("Unable to find definition for "+type.get(0).getCode());
throw new FHIRException("Unable to find definition for "+type.get(0).getWorkingCode());
for (int i = 1; i < type.size(); i++) {
StructureDefinition t = library.get(type.get(i).getCode());
StructureDefinition t = library.get(type.get(i).getWorkingCode());
if (t == null)
throw new FHIRException("Unable to find definition for "+type.get(i).getCode());
throw new FHIRException("Unable to find definition for "+type.get(i).getWorkingCode());
sd = getCommonAncestor(sd, t);
}
return sd;
@ -520,10 +520,11 @@ public class XmlSchemaGenerator {
// if (!max.equals("1"))
// throw new FHIRException("Illegal cardinality \""+max+"\" for attribute "+edc.getPath());
if (Utilities.isAbsoluteUrl(t.getCode()))
throw new FHIRException("Only FHIR primitive types are supported for attributes ("+t.getCode()+")");
String tc = t.getWorkingCode();
if (Utilities.isAbsoluteUrl(tc))
throw new FHIRException("Only FHIR primitive types are supported for attributes ("+tc+")");
String typeNs = namespaces.get("http://hl7.org/fhir");
String type = t.getCode();
String type = tc;
w(" <xs:attribute name=\""+name+"\" use=\""+(min.equals("0") || edc.hasFixed() || edc.hasDefaultValue() ? "optional" : "required")+"\" type=\""+typeNs+":"+type+(typeNs.equals("fhir") ? "-primitive" : "")+"\""+
(edc.hasFixed() ? " fixed=\""+edc.getFixed().primitiveValue()+"\"" : "")+(edc.hasDefaultValue() && !edc.hasFixed() ? " default=\""+edc.getDefaultValue().primitiveValue()+"\"" : "")+"");

View File

@ -501,7 +501,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
if (options != null)
updateParameters(options, pIn);
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId());
@ -511,8 +511,9 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return res;
}
public void updateParameters(TerminologyServiceOptions options, Parameters pIn) {
if (isNotBlank(options.getLanguage())) {
private void setTerminologyOptions(TerminologyServiceOptions options, Parameters pIn) {
if (options != null) {
if (!Utilities.noString(options.getLanguage()))
pIn.addParameter("displayLanguage", options.getLanguage());
}
}
@ -541,7 +542,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code);
if (options != null)
updateParameters(options, pIn);
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());
@ -964,6 +965,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
result.addAll(maps.values());
result.addAll(transforms.values());
result.addAll(plans.values());
result.addAll(questionnaires.values());
return result;
}
}
@ -1114,5 +1116,59 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return res;
}
public String getLinkForUrl(String corePath, String url) {
for (CodeSystem r : codeSystems.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (ValueSet r : valueSets.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (ConceptMap r : maps.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (StructureMap r : transforms.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (StructureDefinition r : structures.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (ImplementationGuide r : guides.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (CapabilityStatement r : capstmts.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (SearchParameter r : searchParameters.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (Questionnaire r : questionnaires.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (OperationDefinition r : operations.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
for (PlanDefinition r : plans.values())
if (url.equals(r.getUrl()))
return r.getUserString("path");
if (url.equals("http://loinc.org"))
return corePath+"loinc.html";
if (url.equals("http://unitsofmeasure.org"))
return corePath+"ucum.html";
if (url.equals("http://snomed.info/sct"))
return corePath+"snomed.html";
return null;
}
}

View File

@ -160,6 +160,11 @@ public interface IWorkerContext {
* It's an error if the second form doesn't agree with class_. It's an
* error if class_ is null for the last form
*
* @param resource
* @param Reference
* @return
* @throws FHIRException
* @throws Exception
*/
public <T extends Resource> T fetchResource(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
@ -262,6 +267,7 @@ public interface IWorkerContext {
/**
* ValueSet Expansion - see $expand, but resolves the binding first
*
* @param source
* @return
* @throws FHIRException
*/
@ -445,4 +451,6 @@ public interface IWorkerContext {
public StructureDefinition fetchTypeDefinition(String typeName);
public void setUcumService(UcumService ucumService);
public String getLinkForUrl(String corePath, String s);
}

View File

@ -604,6 +604,14 @@ public class Element extends Base {
return getNamedChild(name) != null;
}
public boolean hasChildren(String name) {
if (children != null)
for (Element child : children)
if (child.getName().equals(name))
return true;
return false;
}
@Override
public String toString() {
return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";

View File

@ -159,22 +159,7 @@ public class JsonParser extends ParserBase {
// 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) {
if (property.isChoice()) {
for (TypeRefComponent type : property.getDefinition().getType()) {
String eName = property.getName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getCode());
if (!isPrimitive(type.getCode()) && object.has(eName)) {
parseChildComplex(path, object, context, processed, property, eName);
break;
} else if (isPrimitive(type.getCode()) && (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());
}
parseChildItem(path, object, context, processed, property);
}
// second pass: check for things not processed
@ -187,6 +172,25 @@ public class JsonParser extends ParserBase {
}
}
public void parseChildItem(String path, JsonObject object, Element context, Set<String> processed, Property property) {
if (property.isChoice()) {
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 context, Set<String> processed, Property property, String name) throws FHIRException {
processed.add(name);
String npath = path+"/"+property.getName();

View File

@ -64,14 +64,14 @@ public class Property {
if (definition.getType().size() == 0)
return null;
else if (definition.getType().size() > 1) {
String tn = definition.getType().get(0).getCode();
String tn = definition.getType().get(0).getWorkingCode();
for (int i = 1; i < definition.getType().size(); i++) {
if (!tn.equals(definition.getType().get(i).getCode()))
if (!tn.equals(definition.getType().get(i).getWorkingCode()))
throw new Error("logic error, gettype when types > 1");
}
return tn;
} else
return definition.getType().get(0).getCode();
return definition.getType().get(0).getWorkingCode();
}
public String getType(String elementName) {
@ -114,7 +114,7 @@ public class Property {
else
return structure.getId();
} else
return ed.getType().get(0).getCode();
return ed.getType().get(0).getWorkingCode();
}
public boolean hasType(String elementName) {
@ -251,14 +251,14 @@ public class Property {
// ok, find the right definitions
String t = null;
if (ed.getType().size() == 1)
t = ed.getType().get(0).getCode();
t = ed.getType().get(0).getWorkingCode();
else if (ed.getType().size() == 0)
throw new Error("types == 0, and no children found on "+getDefinition().getPath());
else {
t = ed.getType().get(0).getCode();
t = ed.getType().get(0).getWorkingCode();
boolean all = true;
for (TypeRefComponent tr : ed.getType()) {
if (!tr.getCode().equals(t)) {
if (!tr.getWorkingCode().equals(t)) {
all = false;
break;
}
@ -271,12 +271,12 @@ public class Property {
t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype");
boolean ok = false;
for (TypeRefComponent tr : ed.getType()) {
if (tr.getCode().equals(t))
if (tr.getWorkingCode().equals(t))
ok = true;
if (Utilities.isAbsoluteUrl(tr.getCode())) {
StructureDefinition sdt = context.fetchResource(StructureDefinition.class, tr.getCode());
if (Utilities.isAbsoluteUrl(tr.getWorkingCode())) {
StructureDefinition sdt = context.fetchResource(StructureDefinition.class, tr.getWorkingCode());
if (sdt != null && sdt.getType().equals(t)) {
url = tr.getCode();
url = tr.getWorkingCode();
ok = true;
}
}
@ -295,7 +295,7 @@ public class Property {
}
if (!"xhtml".equals(t)) {
for (TypeRefComponent aType: ed.getType()) {
if (aType.getCode().equals(t)) {
if (aType.getWorkingCode().equals(t)) {
if (aType.hasProfile()) {
assert aType.getProfile().size() == 1;
url = aType.getProfile().get(0).getValue();

View File

@ -59,4 +59,7 @@ public class Constants {
public final static String BUILD_ID = "e0e3caf9ba";
public final static String DATE = "Thu Dec 13 14:07:26 AEDT 2018";
public final static String URI_REGEX = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BiologicallyDerivedProduct|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|CatalogEntry|ChargeItem|ChargeItemDefinition|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|Consent|Contract|Coverage|CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceDefinition|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EffectEvidenceSynthesis|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceVariable|ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|ImplementationGuide|InsurancePlan|Invoice|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|MedicinalProduct|MedicinalProductAuthorization|MedicinalProductContraindication|MedicinalProductIndication|MedicinalProductIngredient|MedicinalProductInteraction|MedicinalProductManufactured|MedicinalProductPackaged|MedicinalProductPharmaceutical|MedicinalProductUndesirableEffect|MessageDefinition|MessageHeader|MolecularSequence|NamingSystem|NutritionOrder|Observation|ObservationDefinition|OperationDefinition|OperationOutcome|Organization|OrganizationAffiliation|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|RelatedPerson|RequestGroup|ResearchDefinition|ResearchElementDefinition|ResearchStudy|ResearchSubject|RiskAssessment|RiskEvidenceSynthesis|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|StructureDefinition|StructureMap|Subscription|Substance|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|SubstanceReferenceInformation|SubstanceSourceMaterial|SubstanceSpecification|SupplyDelivery|SupplyRequest|Task|TerminologyCapabilities|TestReport|TestScript|ValueSet|VerificationResult|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?";
public final static String LOCAL_REF_REGEX = "(Account|ActivityDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BiologicallyDerivedProduct|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|CatalogEntry|ChargeItem|ChargeItemDefinition|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|Consent|Contract|Coverage|CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceDefinition|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EffectEvidenceSynthesis|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceVariable|ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|ImplementationGuide|InsurancePlan|Invoice|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|MedicinalProduct|MedicinalProductAuthorization|MedicinalProductContraindication|MedicinalProductIndication|MedicinalProductIngredient|MedicinalProductInteraction|MedicinalProductManufactured|MedicinalProductPackaged|MedicinalProductPharmaceutical|MedicinalProductUndesirableEffect|MessageDefinition|MessageHeader|MolecularSequence|NamingSystem|NutritionOrder|Observation|ObservationDefinition|OperationDefinition|OperationOutcome|Organization|OrganizationAffiliation|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|RelatedPerson|RequestGroup|ResearchDefinition|ResearchElementDefinition|ResearchStudy|ResearchSubject|RiskAssessment|RiskEvidenceSynthesis|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|StructureDefinition|StructureMap|Subscription|Substance|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|SubstanceReferenceInformation|SubstanceSourceMaterial|SubstanceSpecification|SupplyDelivery|SupplyRequest|Task|TerminologyCapabilities|TestReport|TestScript|ValueSet|VerificationResult|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}";
public final static String NS_SYSTEM_TYPE = "http://hl7.org/fhirpath/System.";
}

View File

@ -58,6 +58,7 @@ import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
import org.hl7.fhir.r4.model.Enumerations.BindingStrengthEnumFactory;
import org.hl7.fhir.r4.utils.ToolingExtensions;
// added from java-adornments.txt:
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
@ -2256,6 +2257,50 @@ public class ElementDefinition extends BackboneType implements ICompositeType {
return Utilities.existsInList(getCode(), "Reference", "canonical");
}
/**
* This code checks for the system prefix and returns the FHIR type
*
* @return
*/
public String getWorkingCode() {
if (hasExtension(ToolingExtensions.EXT_FHIR_TYPE))
return getExtensionString(ToolingExtensions.EXT_FHIR_TYPE);
if (!hasCodeElement())
return null;
if (getCodeElement().hasExtension(ToolingExtensions.EXT_XML_TYPE)) {
String s = getCodeElement().getExtensionString(ToolingExtensions.EXT_XML_TYPE);
if ("xsd:gYear OR xsd:gYearMonth OR xsd:date OR xsd:dateTime".equals(s))
return "dateTime";
if ("xsd:gYear OR xsd:gYearMonth OR xsd:date".equals(s))
return "date";
if ("xsd:dateTime".equals(s))
return "instant";
if ("xsd:token".equals(s))
return "code";
if ("xsd:boolean".equals(s))
return "boolean";
if ("xsd:string".equals(s))
return "string";
if ("xsd:time".equals(s))
return "time";
if ("xsd:int".equals(s))
return "integer";
if ("xsd:decimal OR xsd:double".equals(s))
return "decimal";
if ("xsd:base64Binary".equals(s))
return "base64Binary";
if ("xsd:positiveInteger".equals(s))
return "positiveInt";
if ("xsd:nonNegativeInteger".equals(s))
return "unsignedInt";
if ("xsd:anyURI".equals(s))
return "uri";
throw new Error("Unknown xml type '"+s+"'");
}
return getCode();
}
// end addition
}

View File

@ -1975,6 +1975,42 @@ public class Questionnaire extends MetadataResource {
}
public QuestionnaireItemComponent getQuestion(String linkId) {
if (linkId == null)
return null;
for (QuestionnaireItemComponent i : getItem()) {
if (i.getLinkId().equals(linkId))
return i;
QuestionnaireItemComponent t = i.getQuestion(linkId);
if (t != null)
return t;
}
return null;
}
public QuestionnaireItemComponent getCommonGroup(QuestionnaireItemComponent q1, QuestionnaireItemComponent q2) {
if (q1 == null || q2 == null)
return null;
for (QuestionnaireItemComponent i : getItem()) {
QuestionnaireItemComponent t = i.getCommonGroup(q1, q2);
if (t != null)
return t;
}
if (containsQuestion(q1) && containsQuestion(q2))
return this;
return null;
}
public boolean containsQuestion(QuestionnaireItemComponent q) {
if (q == this)
return true;
for (QuestionnaireItemComponent i : getItem()) {
if (i.containsQuestion(q))
return true;
}
return false;
}
}
@Block()
@ -5222,6 +5258,29 @@ public class Questionnaire extends MetadataResource {
*/
public static final ca.uhn.fhir.rest.gclient.TokenClientParam STATUS = new ca.uhn.fhir.rest.gclient.TokenClientParam(SP_STATUS);
public QuestionnaireItemComponent getQuestion(String linkId) {
if (linkId == null)
return null;
for (QuestionnaireItemComponent i : getItem()) {
if (i.getLinkId().equals(linkId))
return i;
QuestionnaireItemComponent t = i.getQuestion(linkId);
if (t != null)
return t;
}
return null;
}
public QuestionnaireItemComponent getCommonGroup(QuestionnaireItemComponent q1, QuestionnaireItemComponent q2) {
for (QuestionnaireItemComponent i : getItem()) {
QuestionnaireItemComponent t = i.getCommonGroup(q1, q2);
if (t != null)
return t;
}
return null;
}
}

View File

@ -166,12 +166,12 @@ public class DefinitionNavigator {
private void loadTypedChildren(TypeRefComponent type) throws DefinitionException {
typeOfChildren = null;
StructureDefinition sd = context.fetchResource(StructureDefinition.class, /* GF#13465 : this somehow needs to be revisited type.hasProfile() ? type.getProfile() : */ type.getCode());
StructureDefinition sd = context.fetchResource(StructureDefinition.class, /* GF#13465 : this somehow needs to be revisited type.hasProfile() ? type.getProfile() : */ type.getWorkingCode());
if (sd != null) {
DefinitionNavigator dn = new DefinitionNavigator(context, sd, 0, path, names, sd.getType());
typeChildren = dn.children();
} else
throw new DefinitionException("Unable to find definition for "+type.getCode()+(type.hasProfile() ? "("+type.getProfile()+")" : ""));
throw new DefinitionException("Unable to find definition for "+type.getWorkingCode()+(type.hasProfile() ? "("+type.getProfile()+")" : ""));
typeOfChildren = type;
}

View File

@ -234,6 +234,11 @@ public class FHIRPathEngine {
public Base resolveReference(Object appContext, String url) throws FHIRException;
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException;
/*
* return the value set referenced by the url, which has been used in memberOf()
*/
public ValueSet resolveValueSet(Object appContext, String url);
}
@ -1164,10 +1169,10 @@ public class FHIRPathEngine {
work = work2;
else if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) {
work2 = executeTypeName(context, focus, next, false);
work = operate(work, last.getOperation(), work2);
work = operate(context, work, last.getOperation(), work2);
} else {
work2 = execute(context, focus, next, true);
work = operate(work, last.getOperation(), work2);
work = operate(context, work, last.getOperation(), work2);
// System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString());
}
last = next;
@ -1390,7 +1395,7 @@ public class FHIRPathEngine {
}
private List<Base> operate(List<Base> left, Operation operation, List<Base> right) throws FHIRException {
private List<Base> operate(ExecutionContext context, List<Base> left, Operation operation, List<Base> right) throws FHIRException {
switch (operation) {
case Equals: return opEquals(left, right);
case Equivalent: return opEquivalent(left, right);
@ -1402,7 +1407,7 @@ public class FHIRPathEngine {
case GreaterOrEqual: return opGreaterOrEqual(left, right);
case Union: return opUnion(left, right);
case In: return opIn(left, right);
case MemberOf: return opMemberOf(left, right);
case MemberOf: return opMemberOf(context, left, right);
case Contains: return opContains(left, right);
case Or: return opOr(left, right);
case And: return opAnd(left, right);
@ -1874,9 +1879,9 @@ public class FHIRPathEngine {
return new ArrayList<Base>();
}
private List<Base> opMemberOf(List<Base> left, List<Base> right) throws FHIRException {
private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right) throws FHIRException {
boolean ans = false;
ValueSet vs = worker.fetchResource(ValueSet.class, right.get(0).primitiveValue());
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, right.get(0).primitiveValue()) : worker.fetchResource(ValueSet.class, right.get(0).primitiveValue());
if (vs != null) {
for (Base l : left) {
if (l.fhirType().equals("code")) {
@ -3513,8 +3518,10 @@ public class FHIRPathEngine {
String st = convertToString(focus.get(0));
if (Utilities.noString(st))
result.add(new BooleanType(false).noExtensions());
else
result.add(new BooleanType(st.matches(sw)).noExtensions());
else {
boolean ok = st.matches(sw);
result.add(new BooleanType(ok).noExtensions());
}
} else
result.add(new BooleanType(false).noExtensions());
return result;
@ -3908,6 +3915,9 @@ public class FHIRPathEngine {
throw new PathEngineException("No type provided in BuildToolPathEvaluator.getChildTypesByName");
if (type.equals("http://hl7.org/fhir/StructureDefinition/xhtml"))
return;
if (type.startsWith(Constants.NS_SYSTEM_TYPE))
return;
if (type.equals(TypeDetails.FP_SimpleTypeInfo)) {
getSimpleTypeChildTypesByName(name, result);
} else if (type.equals(TypeDetails.FP_ClassInfo)) {
@ -3999,7 +4009,7 @@ public class FHIRPathEngine {
else
for (TypeRefComponent t : ed.getDefinition().getType()) {
if (Utilities.noString(t.getCode())) {
if (Utilities.existsInList(ed.getDefinition().getId(), "Element.id", "Extension.url"))
if (Utilities.existsInList(ed.getDefinition().getId(), "Element.id", "Extension.url") || Utilities.existsInList(ed.getDefinition().getBase().getPath(), "Resource.id", "Element.id", "Extension.url"))
result.addType(TypeDetails.FP_NS, "string");
break; // throw new PathEngineException("Illegal reference to primitive value attribute @ "+path);
}
@ -4317,5 +4327,10 @@ public class FHIRPathEngine {
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public IWorkerContext getWorker() {
return worker;
}
}

View File

@ -297,18 +297,18 @@ public class GraphQLSchemaGenerator {
private void generateProperty(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition child, TypeRefComponent typeDetails, boolean suffix, ElementDefinition cr, String mode, String suffixS) throws IOException {
if (isPrimitive(typeDetails)) {
String n = getGqlname(typeDetails.getCode());
String n = getGqlname(typeDetails.getWorkingCode());
b.append(" ");
b.append(tail(child.getPath(), suffix));
if (suffix)
b.append(Utilities.capitalize(typeDetails.getCode()));
b.append(Utilities.capitalize(typeDetails.getWorkingCode()));
b.append(": ");
b.append(n);
if (!child.getPath().endsWith(".id")) {
b.append(" _");
b.append(tail(child.getPath(), suffix));
if (suffix)
b.append(Utilities.capitalize(typeDetails.getCode()));
b.append(Utilities.capitalize(typeDetails.getWorkingCode()));
if (!child.getMax().equals("1"))
b.append(": [ElementBase]\r\n");
else
@ -319,11 +319,11 @@ public class GraphQLSchemaGenerator {
b.append(" ");
b.append(tail(child.getPath(), suffix));
if (suffix)
b.append(Utilities.capitalize(typeDetails.getCode()));
b.append(Utilities.capitalize(typeDetails.getWorkingCode()));
b.append(": ");
if (!child.getMax().equals("1"))
b.append("[");
String type = typeDetails.getCode();
String type = typeDetails.getWorkingCode();
if (cr != null)
b.append(generateInnerType(list, sd, typeName, cr, mode, suffixS));
else if (Utilities.existsInList(type, "Element", "BackboneElement"))
@ -366,7 +366,7 @@ public class GraphQLSchemaGenerator {
}
private boolean isPrimitive(TypeRefComponent type) {
String typeName = type.getCode();
String typeName = type.getWorkingCode();
StructureDefinition sd = context.fetchTypeDefinition(typeName);
if (sd == null)
return false;

View File

@ -33,6 +33,7 @@ import org.hl7.fhir.r4.model.ExpressionNode;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.Tuple;
import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.utils.FHIRPathEngine.ExpressionNodeWithOffset;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.utilities.Utilities;
@ -417,4 +418,13 @@ public class LiquidEngine implements IEvaluationContext {
return conformsToProfile(ctxt.externalContext, item, url);
}
@Override
public ValueSet resolveValueSet(Object appContext, String url) {
LiquidEngineContext ctxt = (LiquidEngineContext) appContext;
if (externalHostServices != null)
return externalHostServices.resolveValueSet(ctxt.externalContext, url);
else
return engine.getWorker().fetchResource(ValueSet.class, url);
}
}

View File

@ -77,6 +77,7 @@ import org.hl7.fhir.utilities.xml.XmlGenerator;
import org.w3c.dom.Element;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
@ -307,29 +308,29 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private interface PropertyWrapper {
String getName();
boolean hasValues();
List<BaseWrapper> getValues();
String getTypeCode();
String getDefinition();
int getMinCardinality();
int getMaxCardinality();
StructureDefinition getStructure();
BaseWrapper value();
public String getName();
public boolean hasValues();
public List<BaseWrapper> getValues();
public String getTypeCode();
public String getDefinition();
public int getMinCardinality();
public int getMaxCardinality();
public StructureDefinition getStructure();
public BaseWrapper value();
}
private interface ResourceWrapper {
List<ResourceWrapper> getContained();
String getId();
XhtmlNode getNarrative() throws IOException, FHIRException;
String getName();
List<PropertyWrapper> children();
public List<ResourceWrapper> getContained();
public String getId();
public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException;
public String getName();
public List<PropertyWrapper> children();
}
private interface BaseWrapper {
Base getBase() throws IOException, FHIRException;
List<PropertyWrapper> children();
PropertyWrapper getChildByName(String tail);
public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException;
public List<PropertyWrapper> children();
public PropertyWrapper getChildByName(String tail);
}
private class BaseWrapperElement implements BaseWrapper {
@ -348,7 +349,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
@Override
public Base getBase() throws IOException, FHIRException {
public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException {
if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
return null;
@ -421,9 +422,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (definition.getType().isEmpty())
return null;
if (definition.getType().size() == 1) {
if (definition.getType().get(0).getCode().equals("Element") || definition.getType().get(0).getCode().equals("BackboneElement"))
if (definition.getType().get(0).getWorkingCode().equals("Element") || definition.getType().get(0).getWorkingCode().equals("BackboneElement"))
return null;
return definition.getType().get(0).getCode();
return definition.getType().get(0).getWorkingCode();
}
String t = e.getNodeName().substring(tail(definition.getPath()).length()-3);
@ -442,7 +443,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
public String getTypeCode() {
if (definition == null || definition.getType().size() != 1)
throw new Error("not handled");
return definition.getType().get(0).getCode();
return definition.getType().get(0).getWorkingCode();
}
@Override
@ -496,7 +497,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
@Override
public Base getBase() throws IOException, FHIRException {
public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException {
if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
return null;
@ -699,7 +700,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
@Override
public XhtmlNode getNarrative() throws IOException, FHIRException {
public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException {
Element txt = XMLUtil.getNamedChild(wrapped, "text");
if (txt == null)
return null;
@ -1058,7 +1059,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return b;
}
private void generateByProfile(Element eres, StructureDefinition profile, Element ee, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, IOException {
private void generateByProfile(Element eres, StructureDefinition profile, Element ee, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException {
ResourceWrapperElement resw = new ResourceWrapperElement(eres, profile);
BaseWrapperElement base = new BaseWrapperElement(ee, null, profile, profile.getSnapshot().getElement().get(0));
@ -1066,11 +1067,11 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private void generateByProfile(Resource res, StructureDefinition profile, Base e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, ResourceContext rc) throws FHIRException, IOException {
private void generateByProfile(Resource res, StructureDefinition profile, Base e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException {
generateByProfile(new ResourceWrapperDirect(res), profile, new BaseWrapperDirect(e), allElements, defn, children, x, path, showCodeDetails, 0, rc);
}
private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent, ResourceContext rc) throws FHIRException, IOException {
private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException {
if (children.isEmpty()) {
renderLeaf(res, e, defn, x, false, showCodeDetails, readDisplayHints(defn), path, indent, rc);
} else {
@ -1160,7 +1161,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
grandChildren.removeAll(toRemove);
}
private List<PropertyWrapper> splitExtensions(StructureDefinition profile, List<PropertyWrapper> children) throws IOException, FHIRException {
private List<PropertyWrapper> splitExtensions(StructureDefinition profile, List<PropertyWrapper> children) throws UnsupportedEncodingException, IOException, FHIRException {
List<PropertyWrapper> results = new ArrayList<PropertyWrapper>();
Map<String, PropertyWrapper> map = new HashMap<String, PropertyWrapper>();
for (PropertyWrapper p : children)
@ -1196,7 +1197,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
@SuppressWarnings("rawtypes")
private boolean isDefaultValue(Map<String, String> displayHints, List<BaseWrapper> list) throws IOException, FHIRException {
private boolean isDefaultValue(Map<String, String> displayHints, List<BaseWrapper> list) throws UnsupportedEncodingException, IOException, FHIRException {
if (list.size() != 1)
return false;
if (list.get(0).getBase() instanceof PrimitiveType)
@ -1207,7 +1208,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
private boolean isDefault(Map<String, String> displayHints, PrimitiveType primitiveType) {
String v = primitiveType.asStringValue();
return !Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default"));
if (!Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default")))
return true;
return false;
}
private boolean exemptFromRendering(ElementDefinition child) {
@ -1215,13 +1218,16 @@ public class NarrativeGenerator implements INarrativeGenerator {
return false;
if ("Composition.subject".equals(child.getPath()))
return true;
return "Composition.section".equals(child.getPath());
if ("Composition.section".equals(child.getPath()))
return true;
return false;
}
private boolean renderAsList(ElementDefinition child) {
if (child.getType().size() == 1) {
String t = child.getType().get(0).getCode();
return t.equals("Address") || t.equals("Reference");
String t = child.getType().get(0).getWorkingCode();
if (t.equals("Address") || t.equals("Reference"))
return true;
}
return false;
}
@ -1231,7 +1237,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
tr.td().b().addText(Utilities.capitalize(tail(e.getPath())));
}
private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, IOException {
private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException {
for (ElementDefinition e : grandChildren) {
PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1));
if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null)
@ -1274,7 +1280,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
//we can tell if e is a primitive because it has types
if (e.getType().isEmpty())
return false;
return e.getType().size() != 1 || !isBase(e.getType().get(0).getCode());
if (e.getType().size() == 1 && isBase(e.getType().get(0).getWorkingCode()))
return false;
return true;
// return !e.getType().isEmpty()
}
@ -1291,7 +1299,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return null;
}
private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, IOException {
private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException {
if (ew == null)
return;
@ -1397,7 +1405,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
}
private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, ResourceContext rc) throws FHIRException, IOException {
private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException {
if (ew == null)
return false;
Base e = ew.getBase();
@ -1551,7 +1559,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return s + (!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
}
private void generateResourceSummary(XhtmlNode x, ResourceWrapper res, boolean textAlready, boolean showCodeDetails, ResourceContext rc) throws FHIRException, IOException {
private void generateResourceSummary(XhtmlNode x, ResourceWrapper res, boolean textAlready, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException {
if (!textAlready) {
XhtmlNode div = res.getNarrative();
if (div != null) {
@ -1597,8 +1605,9 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (child.getMustSupport())
return true;
if (child.getType().size() == 1) {
String t = child.getType().get(0).getCode();
return !t.equals("Address") && !t.equals("Contact") && !t.equals("Reference") && !t.equals("Uri") && !t.equals("Url") && !t.equals("Canonical");
String t = child.getType().get(0).getWorkingCode();
if (t.equals("Address") || t.equals("Contact") || t.equals("Reference") || t.equals("Uri") || t.equals("Url") || t.equals("Canonical"))
return false;
}
return true;
}
@ -4026,7 +4035,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
tr = tbl.tr();
tr.td().addText(p.getUse().toString());
tr.td().addText(path+p.getName());
tr.td().addText(p.getMin() +".."+p.getMax());
tr.td().addText(Integer.toString(p.getMin())+".."+p.getMax());
XhtmlNode td = tr.td();
StructureDefinition sd = context.fetchTypeDefinition(p.getType());
if (sd == null)
@ -4044,8 +4053,6 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
} else
td.ah(sd.getUserString("path")).tx(p.hasType() ? p.getType() : "");
if (p.hasSearchType()) {
td.br();
td.tx("(");
@ -4263,7 +4270,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
for (SectionComponent section : sections) {
node.hr();
if (section.hasTitleElement())
node.addTag("h"+ level).addText(section.getTitle());
node.addTag("h"+Integer.toString(level)).addText(section.getTitle());
// else if (section.hasCode())
// node.addTag("h"+Integer.toString(level)).addText(displayCodeableConcept(section.getCode()));
@ -4532,7 +4539,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (be.hasResource() && be.getResource().hasId())
root.an(be.getResource().getResourceType().name().toLowerCase() + "_" + be.getResource().getId());
root.hr();
root.para().addText("Entry "+ i +(be.hasFullUrl() ? " - Full URL = " + be.getFullUrl() : ""));
root.para().addText("Entry "+Integer.toString(i)+(be.hasFullUrl() ? " - Full URL = " + be.getFullUrl() : ""));
if (be.hasRequest())
renderRequest(root, be.getRequest());
if (be.hasSearch())

View File

@ -114,18 +114,18 @@ public class ProtoBufGenerator {
if (ed.getType().size() != 1)
fld.type = "Unknown";
else {
StructureDefinition td = context.fetchTypeDefinition(ed.getTypeFirstRep().getCode());
StructureDefinition td = context.fetchTypeDefinition(ed.getTypeFirstRep().getWorkingCode());
if (td == null)
fld.type = "Unresolved";
else if (td.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
fld.type = protoTypeForFhirType(ed.getTypeFirstRep().getCode());
fld.type = protoTypeForFhirType(ed.getTypeFirstRep().getWorkingCode());
fld = new Field();
fld.name = tail(ed.getPath())+"Extra";
fld.repeating = (!ed.getMax().equals("1"));
fld.type = "Primitive";
message.fields.add(fld);
} else
fld.type = ed.getTypeFirstRep().getCode();
fld.type = ed.getTypeFirstRep().getWorkingCode();
}
}
}

View File

@ -316,11 +316,11 @@ public class QuestionnaireBuilder {
}
private boolean isAbstractType(List<TypeRefComponent> type) {
return type.size() == 1 && (type.get(0).getCode().equals("Element") || type.get(0).getCode().equals("BackboneElement"));
return type.size() == 1 && (type.get(0).getWorkingCode().equals("Element") || type.get(0).getWorkingCode().equals("BackboneElement"));
}
private boolean isInlineDataType(List<TypeRefComponent> type) {
return type.size() == 1 && !Utilities.existsInList(type.get(0).getCode(), "code", "string", "id", "oid", "markdown", "uri", "boolean", "decimal", "dateTime", "date", "instant", "time", "CodeableConcept", "Period", "Ratio",
return type.size() == 1 && !Utilities.existsInList(type.get(0).getWorkingCode(), "code", "string", "id", "oid", "markdown", "uri", "boolean", "decimal", "dateTime", "date", "instant", "time", "CodeableConcept", "Period", "Ratio",
"HumanName", "Address", "ContactPoint", "Identifier", "integer", "positiveInt", "unsignedInt", "Coding", "Quantity", "Count", "Age", "Duration",
"Distance", "Money", "Money", "Reference", "Duration", "base64Binary", "Attachment", "Age", "Range", "Timing", "Annotation", "SampledData", "Extension",
"SampledData", "Narrative", "Resource", "Meta", "url", "canonical");
@ -330,7 +330,7 @@ public class QuestionnaireBuilder {
String n = tail(child.getPath());
String t = "";
if (!element.getType().isEmpty())
t = element.getType().get(0).getCode();
t = element.getType().get(0).getWorkingCode();
// we don't generate questions for the base stuff in every element
if (t.equals("Resource") && (n.equals("text") || n.equals("language") || n.equals("contained")))
@ -385,7 +385,7 @@ public class QuestionnaireBuilder {
else
ToolingExtensions.addFlyOver(group, element.getDefinition());
if (element.getType().size() > 1 || element.getType().get(0).getCode().equals("*")) {
if (element.getType().size() > 1 || element.getType().get(0).getWorkingCode().equals("*")) {
List<TypeRefComponent> types = expandTypeList(element.getType());
Questionnaire.QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.CHOICE, element.getPath(), "_type", "type", null, makeTypeList(profile, types, element.getPath()));
for (TypeRefComponent t : types) {
@ -410,7 +410,7 @@ public class QuestionnaireBuilder {
for (TypeRefComponent t : types) {
if (t.hasProfile())
result.add(t);
else if (t.getCode().equals("*")) {
else if (t.getWorkingCode().equals("*")) {
result.add(new TypeRefComponent().setCode("integer"));
result.add(new TypeRefComponent().setCode("decimal"));
result.add(new TypeRefComponent().setCode("dateTime"));
@ -459,8 +459,8 @@ public class QuestionnaireBuilder {
}
} else if (!t.hasProfile()) {
ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains();
cc.setCode(t.getCode());
cc.setDisplay(t.getCode());
cc.setCode(t.getWorkingCode());
cc.setDisplay(t.getWorkingCode());
cc.setSystem("http://hl7.org/fhir/data-types");
} else for (UriType u : t.getProfile()) {
ProfileUtilities pu = new ProfileUtilities(context, null, null);
@ -514,7 +514,7 @@ public class QuestionnaireBuilder {
cc.setCode(t.getProfile().get(0).getValue());
cc.setSystem("http://hl7.org/fhir/resource-types");
} else {
cc.setCode(t.getCode());
cc.setCode(t.getWorkingCode());
cc.setSystem("http://hl7.org/fhir/data-types");
}
}
@ -529,7 +529,7 @@ public class QuestionnaireBuilder {
}
private boolean instanceOf(TypeRefComponent t, Element obj) {
if (t.getCode().equals("Reference")) {
if (t.getWorkingCode().equals("Reference")) {
if (!(obj instanceof Reference)) {
return false;
} else {
@ -542,7 +542,7 @@ public class QuestionnaireBuilder {
else
return true;
}
} else if (t.getCode().equals("Quantity")) {
} else if (t.getWorkingCode().equals("Quantity")) {
return obj instanceof Quantity;
} else
throw new NotImplementedException("Not Done Yet");
@ -703,77 +703,78 @@ public class QuestionnaireBuilder {
}
private boolean isPrimitive(TypeRefComponent t) {
String code = t.getCode();
String code = t.getWorkingCode();
StructureDefinition sd = context.fetchTypeDefinition(code);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
private void processDataType(StructureDefinition profile, QuestionnaireItemComponent group, ElementDefinition element, String path, TypeRefComponent t, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<ElementDefinition> parents) throws FHIRException {
if (t.getCode().equals("code"))
String tc = t.getWorkingCode();
if (tc.equals("code"))
addCodeQuestions(group, element, path, answerGroups);
else if (Utilities.existsInList(t.getCode(), "string", "id", "oid", "uuid", "markdown"))
else if (Utilities.existsInList(tc, "string", "id", "oid", "uuid", "markdown"))
addStringQuestions(group, element, path, answerGroups);
else if (Utilities.existsInList(t.getCode(), "uri", "url", "canonical"))
else if (Utilities.existsInList(tc, "uri", "url", "canonical"))
addUriQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("boolean"))
else if (tc.equals("boolean"))
addBooleanQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("decimal"))
else if (tc.equals("decimal"))
addDecimalQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("dateTime") || t.getCode().equals("date"))
else if (tc.equals("dateTime") || tc.equals("date"))
addDateTimeQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("instant"))
else if (tc.equals("instant"))
addInstantQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("time"))
else if (tc.equals("time"))
addTimeQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("CodeableConcept"))
else if (tc.equals("CodeableConcept"))
addCodeableConceptQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Period"))
else if (tc.equals("Period"))
addPeriodQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Ratio"))
else if (tc.equals("Ratio"))
addRatioQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("HumanName"))
else if (tc.equals("HumanName"))
addHumanNameQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Address"))
else if (tc.equals("Address"))
addAddressQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("ContactPoint"))
else if (tc.equals("ContactPoint"))
addContactPointQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Identifier"))
else if (tc.equals("Identifier"))
addIdentifierQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("integer") || t.getCode().equals("positiveInt") || t.getCode().equals("unsignedInt") )
else if (tc.equals("integer") || tc.equals("positiveInt") || tc.equals("unsignedInt") )
addIntegerQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Coding"))
else if (tc.equals("Coding"))
addCodingQuestions(group, element, path, answerGroups);
else if (Utilities.existsInList(t.getCode(), "Quantity", "Count", "Age", "Duration", "Distance", "Money"))
else if (Utilities.existsInList(tc, "Quantity", "Count", "Age", "Duration", "Distance", "Money"))
addQuantityQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Money"))
else if (tc.equals("Money"))
addMoneyQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Reference"))
else if (tc.equals("Reference"))
addReferenceQuestions(group, element, path, t.getTargetProfile(), answerGroups);
else if (t.getCode().equals("Duration"))
else if (tc.equals("Duration"))
addDurationQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("base64Binary"))
else if (tc.equals("base64Binary"))
addBinaryQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Attachment"))
else if (tc.equals("Attachment"))
addAttachmentQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Age"))
else if (tc.equals("Age"))
addAgeQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Range"))
else if (tc.equals("Range"))
addRangeQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Timing"))
else if (tc.equals("Timing"))
addTimingQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Annotation"))
else if (tc.equals("Annotation"))
addAnnotationQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("SampledData"))
else if (tc.equals("SampledData"))
addSampledDataQuestions(group, element, path, answerGroups);
else if (t.getCode().equals("Extension")) {
else if (tc.equals("Extension")) {
if (t.hasProfile())
addExtensionQuestions(profile, group, element, path, t.getProfile().get(0).getValue(), answerGroups, parents);
} else if (t.getCode().equals("SampledData"))
} else if (tc.equals("SampledData"))
addSampledDataQuestions(group, element, path, answerGroups);
else if (!t.getCode().equals("Narrative") && !t.getCode().equals("Resource") && !t.getCode().equals("Meta") && !t.getCode().equals("Signature")) {
StructureDefinition sd = context.fetchTypeDefinition(t.getCode());
else if (!tc.equals("Narrative") && !tc.equals("Resource") && !tc.equals("Meta") && !tc.equals("Signature")) {
StructureDefinition sd = context.fetchTypeDefinition(tc);
if (sd == null)
throw new NotImplementedException("Unhandled Data Type: "+t.getCode()+" on element "+element.getPath());
throw new NotImplementedException("Unhandled Data Type: "+tc+" on element "+element.getPath());
buildGroup(group, sd, sd.getSnapshot().getElementFirstRep(), parents, answerGroups);
}
}

View File

@ -363,7 +363,7 @@ public class ResourceUtilities {
private static String renderType(TypeRefComponent type) {
if (type == null || type.isEmpty())
return "";
return type.getCode();
return type.getWorkingCode();
}
public static void renderContactPoint(StringBuilder b, ContactPoint cp) {

View File

@ -217,6 +217,11 @@ public class StructureMapUtilities {
throw new NotImplementedException("Not done yet (FFHIRPathHostServices.conformsToProfile), when item is element");
}
@Override
public ValueSet resolveValueSet(Object appContext, String url) {
throw new Error("Not Implemented Yet");
}
}
private IWorkerContext worker;
private FHIRPathEngine fpe;
@ -2447,7 +2452,7 @@ public class StructureMapUtilities {
for (TypeRefComponent tr : element.getDefinition().getType()) {
if (!tr.hasCode())
throw new Error("Rule \""+ruleId+"\": Element has no type");
ProfiledType pt = new ProfiledType(tr.getCode());
ProfiledType pt = new ProfiledType(tr.getWorkingCode());
if (tr.hasProfile())
pt.addProfiles(tr.getProfile());
if (element.getDefinition().hasBinding())
@ -2705,11 +2710,11 @@ public class StructureMapUtilities {
private String checkType(String t, Property pvb, List<String> profiles) throws FHIRException {
if (pvb.getDefinition().getType().size() == 1 && isCompatibleType(t, pvb.getDefinition().getType().get(0).getCode()) && profilesMatch(profiles, pvb.getDefinition().getType().get(0).getProfile()))
if (pvb.getDefinition().getType().size() == 1 && isCompatibleType(t, pvb.getDefinition().getType().get(0).getWorkingCode()) && profilesMatch(profiles, pvb.getDefinition().getType().get(0).getProfile()))
return null;
for (TypeRefComponent tr : pvb.getDefinition().getType()) {
if (isCompatibleType(t, tr.getCode()))
return tr.getCode(); // note what is returned - the base type, not the inferred mapping type
if (isCompatibleType(t, tr.getWorkingCode()))
return tr.getWorkingCode(); // note what is returned - the base type, not the inferred mapping type
}
throw new FHIRException("The type "+t+" is not compatible with the allowed types for "+pvb.getDefinition().getPath());
}

View File

@ -105,9 +105,6 @@ public class ToolingExtensions {
public static final String EXT_ISSUE_COL = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col";
public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint";
public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type";
public static final String EXT_RDF_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-rdf-type";
public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/regex";
public static final String EXT_CONTROL = "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl";
public static final String EXT_MINOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs";
@ -163,6 +160,8 @@ public class ToolingExtensions {
public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality";
public static final String EXT_PRIVATE_BASE = "http://hl7.org/fhir/tools/";
public static final String EXT_ALLOWED_TYPE = "http://hl7.org/fhir/StructureDefinition/operationdefinition-allowed-type";
public static final String EXT_FHIR_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type";
public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
// specific extension helpers
@ -214,6 +213,16 @@ public class ToolingExtensions {
}
}
public static void addCodeExtension(Element e, String url, String content) {
if (!StringUtils.isBlank(content)) {
Extension ex = getExtension(e, url);
if (ex != null)
ex.setValue(new CodeType(content));
else
e.getExtension().add(Factory.newExtension(url, new CodeType(content), true));
}
}
public static void addStringExtension(DomainResource e, String url, String content) {
if (!StringUtils.isBlank(content)) {
Extension ex = getExtension(e, url);

View File

@ -143,6 +143,7 @@ public class TypesUtilities {
// special cases
res.add(new WildcardInformation("Dosage", TypeClassification.SPECIAL));
res.add(new WildcardInformation("Meta", TypeClassification.SPECIAL));
return res;
}

View File

@ -32,18 +32,18 @@ public class ValidationProfileSet {
public static class ProfileRegistration {
private String profile;
private boolean error;
private boolean errorOnMissing;
public ProfileRegistration(String profile, boolean error) {
public ProfileRegistration(String profile, boolean errorOnMissing) {
super();
this.profile = profile;
this.error = error;
this.errorOnMissing = errorOnMissing;
}
public String getProfile() {
return profile;
}
public boolean isError() {
return error;
public boolean isErrorOnMissing() {
return errorOnMissing;
}

View File

@ -230,7 +230,7 @@ public class CSVWriter extends TextStreamWriter {
val = o.toString();
} else if (o instanceof TypeRefComponent) {
TypeRefComponent t = (TypeRefComponent)o;
val = t.getCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}") + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
val = t.getWorkingCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}") + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
} else if (o instanceof Coding) {
Coding t = (Coding)o;
val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");

View File

@ -53,8 +53,10 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.ElementDefinition.AggregationMode;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionMappingComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
@ -65,6 +67,7 @@ import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionMappingComponent;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextStreamWriter;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAutoFilter;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCustomFilter;
@ -84,6 +87,7 @@ public class XLSXWriter extends TextStreamWriter {
private XmlParser xml = new XmlParser();
private JsonParser json = new JsonParser();
private boolean asXml;
private boolean hideMustSupportFalse;
private static String[] titles = {
"Path", "Slice Name", "Alias(s)", "Label", "Min", "Max", "Must Support?", "Is Modifier?", "Is Summary?", "Type(s)", "Short",
@ -92,11 +96,12 @@ public class XLSXWriter extends TextStreamWriter {
"Slicing Discriminator", "Slicing Description", "Slicing Ordered", "Slicing Rules", "Base Path", "Base Min", "Base Max",
"Condition(s)", "Constraint(s)"};
public XLSXWriter(OutputStream out, StructureDefinition def, boolean asXml) throws UnsupportedEncodingException {
public XLSXWriter(OutputStream out, StructureDefinition def, boolean asXml, boolean hideMustSupportFalse) throws UnsupportedEncodingException {
super(out);
outStream = out;
this.asXml = asXml;
this.def = def;
this.hideMustSupportFalse = hideMustSupportFalse;
sheet = wb.createSheet("Elements");
styles = createStyles(wb);
Row headerRow = sheet.createRow(0);
@ -155,7 +160,7 @@ public class XLSXWriter extends TextStreamWriter {
private static CellStyle createBorderedStyle(Workbook wb){
BorderStyle thin = BorderStyle.THIN;
short black = IndexedColors.BLACK.getIndex();
short black = IndexedColors.GREY_50_PERCENT.getIndex();
CellStyle style = wb.createCellStyle();
style.setBorderRight(thin);
@ -262,7 +267,17 @@ public class XLSXWriter extends TextStreamWriter {
val = o.toString();
} else if (o instanceof TypeRefComponent) {
TypeRefComponent t = (TypeRefComponent)o;
val = t.getCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}") + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
val = t.getWorkingCode();
if (val == null)
val = "";
if (val.startsWith("http://hl7.org/fhir/StructureDefinition/"))
val = val.substring(40);
if (t.hasTargetProfile())
val = val+ "(" + canonicalList(t.getTargetProfile()) + ")";
if (t.hasProfile())
val = val + " {" + canonicalList(t.getProfile()) + "}";
if (t.hasAggregation())
val = val + " <<" + aggList(t.getAggregation()) + ">>";
} else if (o instanceof Coding) {
Coding t = (Coding)o;
val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");
@ -285,7 +300,30 @@ public class XLSXWriter extends TextStreamWriter {
return s.toString();
}
private String aggList(List<org.hl7.fhir.r4.model.Enumeration<AggregationMode>> list) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (org.hl7.fhir.r4.model.Enumeration<AggregationMode> c : list)
b.append(c.getValue().toCode());
return b.toString();
}
private String canonicalList(List<CanonicalType> list) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("|");
for (CanonicalType c : list) {
String v = c.getValue();
if (v.startsWith("http://hl7.org/fhir/StructureDefinition/"))
v = v.substring(40);
b.append(v);
}
return b.toString();
}
private String renderType(Type value) throws Exception {
if (value == null)
return "";
if (value.isPrimitive())
return value.primitiveValue();
String s = null;
ByteArrayOutputStream bs = new ByteArrayOutputStream();
if (asXml) {
@ -339,6 +377,7 @@ public class XLSXWriter extends TextStreamWriter {
}
sheet.createFreezePane(2,1);
if (hideMustSupportFalse) {
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
String address = "A2:AI" + Math.max(Integer.valueOf(sheet.getLastRowNum()), 2);
CellRangeAddress[] regions = {
@ -378,6 +417,7 @@ public class XLSXWriter extends TextStreamWriter {
for (Row row : sheet) {
if (row.getRowNum()>0 && (!row.getCell(6).getStringCellValue().equals("Y") || !row.getCell(26).getStringCellValue().isEmpty())) {
((XSSFRow) row).getCTRow().setHidden(true);
}
}
}
sheet.setActiveCell(new CellAddress(sheet.getRow(1).getCell(0)));

View File

@ -24,6 +24,7 @@ import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.test.utils.TestingUtilities;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
@ -90,6 +91,10 @@ public class FHIRPathTests {
}
@Override
public ValueSet resolveValueSet(Object appContext, String url) {
return TestingUtilities.context().fetchResource(ValueSet.class, url);
}
}
private static FHIRPathEngine fp;

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.r4.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
@ -22,6 +23,7 @@ import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.conformance.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.Coding;
@ -39,25 +41,142 @@ import org.hl7.fhir.r4.model.TestScript.SetupActionOperationComponent;
import org.hl7.fhir.r4.model.TestScript.TestActionComponent;
import org.hl7.fhir.r4.model.TestScript.TestScriptFixtureComponent;
import org.hl7.fhir.r4.model.TestScript.TestScriptTestComponent;
import org.hl7.fhir.r4.test.SnapShotGenerationTests.TestFetchMode;
import org.hl7.fhir.r4.test.utils.TestingUtilities;
import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.utils.CodingUtilities;
import org.hl7.fhir.r4.utils.EOperationOutcome;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.NarrativeGenerator;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import junit.framework.Assert;
@RunWith(Parameterized.class)
public class SnapShotGenerationTests {
public enum TestFetchMode {
INPUT,
OUTPUT,
INCLUDE
}
public static class Rule {
private String description;
private String expression;
public Rule(String description, String expression) {
super();
this.description = description;
this.expression = expression;
}
public Rule(Element rule) {
super();
this.description = rule.getAttribute("text");
this.expression = rule.getAttribute("fhirpath");
}
public String getDescription() {
return description;
}
public String getExpression() {
return expression;
}
}
public static class TestDetails {
private String id;
private String include;
private String register;
private boolean gen;
private boolean sort;
private boolean fail;
private List<Rule> rules = new ArrayList<>();
private StructureDefinition source;
private StructureDefinition included;
private StructureDefinition expected;
private StructureDefinition output;
public TestDetails(Element test) {
super();
gen = "true".equals(test.getAttribute("gen"));
sort = "true".equals(test.getAttribute("sort"));
fail = "true".equals(test.getAttribute("fail"));
id = test.getAttribute("id");
include = test.getAttribute("include");
register = test.getAttribute("register");
Element rule = XMLUtil.getFirstChild(test);
while (rule != null && rule.getNodeName().equals("rule")) {
rules.add(new Rule(rule));
rule = XMLUtil.getNextSibling(rule);
}
}
public String getId() {
return id;
}
public boolean isSort() {
return sort;
}
public boolean isGen() {
return gen;
}
public String getInclude() {
return include;
}
public boolean isFail() {
return fail;
}
public StructureDefinition getIncluded() {
return included;
}
public List<Rule> getRules() {
return rules;
}
public StructureDefinition getSource() {
return source;
}
public void setSource(StructureDefinition source) {
this.source = source;
}
public StructureDefinition getExpected() {
return expected;
}
public void setExpected(StructureDefinition expected) {
this.expected = expected;
}
public StructureDefinition getOutput() {
return output;
}
public void setOutput(StructureDefinition output) {
this.output = output;
}
public void load() throws FHIRFormatError, FileNotFoundException, IOException {
if (new File(TestingUtilities.resourceNameToFile("snapshot-generation", id+"-input.json")).exists())
source = (StructureDefinition) new JsonParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("snapshot-generation", id+"-input.json")));
else
source = (StructureDefinition) new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("snapshot-generation", id+"-input.xml")));
if (!fail)
expected = (StructureDefinition) new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("snapshot-generation", id+"-expected.xml")));
if (!Utilities.noString(include))
included = (StructureDefinition) new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("snapshot-generation", include+".xml")));
if (!Utilities.noString(register)) {
included = (StructureDefinition) new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("snapshot-generation", register+".xml")));
}
}
}
public class TestPKP implements ProfileKnowledgeProvider {
@Override
@ -68,13 +187,13 @@ public class SnapShotGenerationTests {
@Override
public boolean isResource(String typeSimple) {
StructureDefinition sd = TestingUtilities.context().fetchTypeDefinition(name);
StructureDefinition sd = TestingUtilities.context().fetchTypeDefinition(typeSimple);
return (sd != null) && (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) && (sd.getKind() == StructureDefinitionKind.RESOURCE);
}
@Override
public boolean hasLinkFor(String typeSimple) {
return isDatatype(name);
return isDatatype(typeSimple);
}
@Override
@ -90,6 +209,14 @@ public class SnapShotGenerationTests {
return br;
}
@Override
public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException {
BindingResolution br = new BindingResolution();
br.url = path+"/something.html";
br.display = "something";
return br;
}
@Override
public String getLinkForProfile(StructureDefinition profile, String url) {
StructureDefinition sd = TestingUtilities.context().fetchResource(StructureDefinition.class, url);
@ -105,7 +232,7 @@ public class SnapShotGenerationTests {
}
@Override
public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException {
public String getLinkForUrl(String corePath, String s) {
// TODO Auto-generated method stub
return null;
}
@ -113,99 +240,42 @@ public class SnapShotGenerationTests {
}
private static class SnapShotGenerationTestsContext implements IEvaluationContext {
private Map<String, Resource> fixtures;
private Map<String, StructureDefinition> snapshots = new HashMap<String, StructureDefinition>();
public TestScript tests;
public void checkTestsDetails() {
if (!"http://hl7.org/fhir/tests/snapshotgeneration".equals(tests.getUrl()))
throw new Error("Wrong URL on test script");
if (!tests.getSetup().isEmpty())
throw new Error("Setup is not supported");
if (!tests.getTeardown().isEmpty())
throw new Error("Teardown is not supported");
Set<String> ids = new HashSet<String>();
Set<String> urls = new HashSet<String>();
for (Resource r : tests.getContained()) {
if (ids.contains(r.getId()))
throw new Error("Unsupported: duplicate contained resource on fixture id "+r.getId());
ids.add(r.getId());
if (r instanceof MetadataResource) {
MetadataResource md = (MetadataResource) r;
if (urls.contains(md.getUrl()))
throw new Error("Unsupported: duplicate canonical url "+md.getUrl()+" on fixture id "+r.getId());
urls.add(md.getUrl());
}
}
for (TestScriptFixtureComponent r : tests.getFixture()) {
if (ids.contains(r.getId()))
throw new Error("Unsupported: duplicate contained resource or fixture id "+r.getId());
ids.add(r.getId());
}
Set<String> names = new HashSet<String>();
for (TestScriptTestComponent test : tests.getTest()) {
if (names.contains(test.getName()))
throw new Error("Unsupported: duplicate name "+test.getName());
names.add(test.getName());
if (test.getAction().size() < 2)
throw new Error("Unsupported: multiple actions required");
if (!test.getActionFirstRep().hasOperation())
throw new Error("Unsupported: first action must be an operation");
for (int i = 0; i < test.getAction().size(); i++) {
// if (!test.getAction().get(i).hasAssert())
// throw new Error("Unsupported: following actions must be an asserts");
TestActionComponent action = test.getAction().get(i);
if (action.hasOperation()) {
SetupActionOperationComponent op = test.getActionFirstRep().getOperation();
if (!CodingUtilities.matches(op.getType(), "http://hl7.org/fhir/testscript-operation-codes", "snapshot")
&& !CodingUtilities.matches(op.getType(), "http://hl7.org/fhir/testscript-operation-codes", "sortDifferential"))
throw new Error("Unsupported action operation type "+CodingUtilities.present(op.getType()));
if (!"StructureDefinition".equals(op.getResource()))
throw new Error("Unsupported action operation resource "+op.getResource());
if (!op.hasResponseId())
throw new Error("Unsupported action operation: no response id");
if (!op.hasSourceId())
throw new Error("Unsupported action operation: no source id");
if (!hasSource(op.getSourceId()))
throw new Error("Unsupported action operation: source id could not be resolved");
} else if (action.hasAssert()) {
SetupActionAssertComponent a = action.getAssert();
if (!a.hasLabel())
throw new Error("Unsupported: actions must have a label");
if (!a.hasDescription())
throw new Error("Unsupported: actions must have a description");
if (!a.hasExpression() && !a.hasResponse())
throw new Error("Unsupported: actions must have an expression or a response");
} else {
throw new Error("Unsupported: Unrecognized action type");
}
}
}
}
private boolean hasSource(String sourceId) {
for (TestScriptFixtureComponent ds : tests.getFixture()) {
if (sourceId.equals(ds.getId()))
return true;
}
for (Resource r : tests.getContained()) {
if (sourceId.equals(r.getId()))
return true;
}
return false;
}
public List<TestDetails> tests = new ArrayList<>();
public Resource fetchFixture(String id) {
if (fixtures.containsKey(id))
return fixtures.get(id);
for (TestScriptFixtureComponent ds : tests.getFixture()) {
if (id.equals(ds.getId()))
throw new Error("not done yet");
TestFetchMode mode = TestFetchMode.INPUT;
if (id.equals("patient"))
return TestingUtilities.context().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient");
if (id.equals("valueset"))
return TestingUtilities.context().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/ValueSet");
if (id.equals("organization"))
return TestingUtilities.context().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Organization");
if (id.equals("operationoutcome"))
return TestingUtilities.context().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/OperationOutcome");
if (id.equals("parameters"))
return TestingUtilities.context().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Parameters");
if (id.contains("-")) {
String[] p = id.split("\\-");
id = p[0];
if (p[1].equals("output"))
mode = TestFetchMode.OUTPUT;
else if (p[1].equals("include"))
mode = TestFetchMode.INCLUDE;
}
for (Resource r : tests.getContained()) {
if (id.equals(r.getId()))
return r;
for (TestDetails td : tests) {
if (td.getId().equals(id))
switch (mode) {
case INPUT: return td.getSource();
case OUTPUT: if (td.getOutput() == null)
throw new FHIRException("Not generated yet");
else
return td.getOutput();
case INCLUDE:
return td.getIncluded();
default:
throw new FHIRException("Not done yet");
}
}
return null;
}
@ -213,12 +283,12 @@ public class SnapShotGenerationTests {
// FHIRPath methods
@Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
return null;
throw new Error("Not implemented yet");
}
@Override
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
return null;
throw new Error("Not implemented yet");
}
@Override
@ -251,8 +321,9 @@ public class SnapShotGenerationTests {
list.add(res);
return list;
}
throw new Error("Could not resolve "+id);
}
return null;
throw new Error("Not implemented yet");
}
@Override
@ -275,195 +346,159 @@ public class SnapShotGenerationTests {
throw new NotImplementedException("Not done yet (IGPublisherHostServices.SnapShotGenerationTestsContext), when item is element");
}
}
public StructureDefinition getByUrl(String url) {
if (url == null)
return null;
for (TestDetails t : tests) {
if (t.expected != null && url.equals(t.expected.getUrl()))
return t.expected;
if (t.included != null && url.equals(t.included.getUrl()))
return t.included;
}
return null;
}
@Override
public ValueSet resolveValueSet(Object appContext, String url) {
throw new Error("Not implemented yet");
}
}
private static FHIRPathEngine fp;
@Parameters(name = "{index}: file {0}")
public static Iterable<Object[]> data() throws ParserConfigurationException, IOException, FHIRFormatError {
public static Iterable<Object[]> data() throws ParserConfigurationException, IOException, FHIRFormatError, SAXException {
SnapShotGenerationTestsContext context = new SnapShotGenerationTestsContext();
System.out.println("Snapshot tests using "+TestingUtilities.resourceNameToFile("snapshot-generation-tests.xml"));
context.tests = (TestScript) new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("snapshot-generation-tests.xml")));
context.checkTestsDetails();
List<Object[]> objects = new ArrayList<Object[]>(context.tests.getTest().size());
for (TestScriptTestComponent e : context.tests.getTest()) {
objects.add(new Object[] { e.getName(), e, context });
Document tests = XMLUtil.parseFileToDom(TestingUtilities.resourceNameToFile("snapshot-generation", "manifest.xml"));
Element test = XMLUtil.getFirstChild(tests.getDocumentElement());
List<Object[]> objects = new ArrayList<Object[]>();
while (test != null && test.getNodeName().equals("test")) {
TestDetails t = new TestDetails(test);
context.tests.add(t);
t.load();
objects.add(new Object[] {t.getId(), t, context });
test = XMLUtil.getNextSibling(test);
}
return objects;
}
private final TestScriptTestComponent test;
private final String name;
private final TestDetails test;
private SnapShotGenerationTestsContext context;
private List<ValidationMessage> messages;
public SnapShotGenerationTests(String name, TestScriptTestComponent e, SnapShotGenerationTestsContext context) {
this.name = name;
this.test = e;
public SnapShotGenerationTests(String id, TestDetails test, SnapShotGenerationTestsContext context) {
this.test = test;
this.context = context;
}
@SuppressWarnings("deprecation")
@Test
public void test() throws FHIRException {
try {
for (Resource cr : context.tests.getContained()) {
if (cr instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) cr;
if (sd.getType().equals("Extension")) {
if (TestingUtilities.context().fetchResource(StructureDefinition.class, sd.getUrl()) == null) {
sd.setUserData("path", "test-"+sd.getId()+".html");
StructureDefinition extd = TestingUtilities.context().fetchResource(StructureDefinition.class, sd.getBaseDefinition());
new ProfileUtilities(TestingUtilities.context(), null, null).generateSnapshot(extd, sd, sd.getUrl(), "http://hl7.org/fhir/R4", sd.getName());
TestingUtilities.context().cacheResource(sd);
debugSaveResource(sd);
}
}
}
public void test() throws Exception {
if (fp == null)
fp = new FHIRPathEngine(TestingUtilities.context());
fp.setHostServices(context);
messages = new ArrayList<ValidationMessage>();
if (test.isFail()) {
try {
if (test.isGen())
testGen();
else
testSort();
Assert.assertTrue("Should have failed", false);
} catch (Throwable e) {
Assert.assertTrue("all ok", true);
}
if (fp == null)
fp = new FHIRPathEngine(TestingUtilities.context());
fp.setHostServices(context);
resolveFixtures();
TestScript.AssertionResponseTypes lastOpOutcome = null;
for (int i = 0; i < test.getAction().size(); i++) {
TestActionComponent action = test.getAction().get(i);
StructureDefinition sdn = new StructureDefinition();
if (action.hasOperation()) {
lastOpOutcome = AssertionResponseTypes.OKAY;
try {
SetupActionOperationComponent op = action.getOperation();
Coding opType = op.getType();
if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("snapshot")) {
StructureDefinition source = (StructureDefinition) context.fetchFixture(op.getSourceId());
StructureDefinition base = getSD(source.getBaseDefinition());
StructureDefinition output = source.copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), null, new TestPKP());
pu.setIds(source, false);
if ("sort=true".equals(op.getParams())) {
List<String> errors = new ArrayList<String>();
int lastCount = output.getDifferential().getElement().size();
pu.sortDifferential(base, output, source.getName(), errors);
if (errors.size() > 0)
throw new FHIRException("Sort failed: "+errors.toString());
}
pu.generateSnapshot(base, output, source.getUrl(), "http://hl7.org/fhir/R4", source.getName());
debugSaveResource(output);
context.fixtures.put(op.getResponseId(), output);
context.snapshots.put(output.getUrl(), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
if (output.getDifferential().hasElement())
new NarrativeGenerator("", "http://hl7.org/fhir", TestingUtilities.context()).setPkp(new TestPKP()).generate(output, null);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+"-d.xml")), output);
} else if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("sortDifferential")) {
StructureDefinition source = (StructureDefinition) context.fetchFixture(op.getSourceId());
StructureDefinition base = getSD(source.getBaseDefinition());
StructureDefinition output = source.copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), null, null);
pu.setIds(source, false);
List<String> errors = new ArrayList<String>();
pu.sortDifferential(base, output, output.getUrl(), errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
context.fixtures.put(op.getResponseId(), output);
context.snapshots.put(output.getUrl(), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
} else if (action.hasAssert()) {
SetupActionAssertComponent a = action.getAssert();
if (a.hasResponse() && a.getResponse().equals(TestScript.AssertionResponseTypes.BAD))
Assert.fail(action.getAssert().getLabel()+": "+action.getAssert().getDescription());
else {
boolean ok = fp.evaluateToBoolean(sdn, sdn, sdn, a.getExpression());
Assert.assertTrue(a.getLabel()+": "+a.getDescription(), ok);
}
} else {
throw new Error("Unsupported operation: " + opType.getSystem() + " : " + opType.getCode());
}
lastOpOutcome = AssertionResponseTypes.OKAY;
} catch (Exception e) {
for (int j = i+1;i < test.getAction().size(); i++) {
TestActionComponent followAction = test.getAction().get(j);
if (followAction.hasAssert() && followAction.getAssert().hasResponse() && followAction.getAssert().getResponse().equals(TestScript.AssertionResponseTypes.BAD)) {
lastOpOutcome = AssertionResponseTypes.BAD;
break;
}
}
}
} else if (action.hasAssert()) {
SetupActionAssertComponent a = action.getAssert();
if (a.getResponse() != null) {
Assert.assertTrue(a.getLabel()+" (response): "+a.getDescription(), a.getResponse() == lastOpOutcome);
}
if (a.hasExpression()) {
boolean ok = fp.evaluateToBoolean(sdn, sdn, sdn, a.getExpression());
Assert.assertTrue(a.getLabel()+": "+a.getDescription(), ok);
}
}
}
} catch (Exception e) {
e.printStackTrace();
throw new FHIRException(e);
} else if (test.isGen())
testGen();
else
testSort();
for (Rule r : test.getRules()) {
StructureDefinition sdn = new StructureDefinition();
boolean ok = fp.evaluateToBoolean(sdn, sdn, sdn, r.expression);
Assert.assertTrue(r.description, ok);
}
}
private void debugSaveResource(Resource r) throws IOException {
String dir = System.getProperty("java.io.tmpdir");
if (new File("c:\\temp").exists())
dir = "c:\\temp";
String fn = Utilities.path(dir, r.fhirType()+"-"+r.getId()+".xml");
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(fn), r);
private void testSort() throws DefinitionException, FHIRException, IOException {
StructureDefinition base = getSD(test.getSource().getBaseDefinition());
test.setOutput(test.getSource().copy());
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), null, null);
pu.setIds(test.getSource(), false);
List<String> errors = new ArrayList<String>();
pu.sortDifferential(base, test.getOutput(), test.getOutput().getUrl(), errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(TestingUtilities.resourceNameToFile("snapshot-generation", test.getId()+"-actual.xml")), test.getOutput());
Assert.assertTrue("Output does not match expected", test.expected.equalsDeep(test.output));
}
private void testGen() throws Exception {
if (!Utilities.noString(test.register)) {
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), null, null);
pu.setNewSlicingProcessing(true);
List<String> errors = new ArrayList<String>();
pu.setIds(test.included, false);
StructureDefinition base = TestingUtilities.context().fetchResource(StructureDefinition.class, test.included.getBaseDefinition());
pu.generateSnapshot(base, test.included, test.included.getUrl(), "http://test.org/profile", test.included.getName());
TestingUtilities.context().cacheResource(test.included);
}
StructureDefinition base = getSD(test.getSource().getBaseDefinition());
if (!base.getUrl().equals(test.getSource().getBaseDefinition()))
throw new Exception("URL mismatch on base: "+base.getUrl()+" wanting "+test.getSource().getBaseDefinition());
StructureDefinition output = test.getSource().copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), messages , new TestPKP());
pu.setNewSlicingProcessing(true);
pu.setThrowException(true);
pu.setDebug(true);
pu.setIds(test.getSource(), false);
if (test.isSort()) {
List<String> errors = new ArrayList<String>();
int lastCount = output.getDifferential().getElement().size();
pu.sortDifferential(base, output, test.getSource().getName(), errors);
if (errors.size() > 0)
throw new FHIRException("Sort failed: "+errors.toString());
}
try {
pu.generateSnapshot(base, output, test.getSource().getUrl(), "http://test.org/profile", test.getSource().getName());
} catch (Throwable e) {
System.out.println("\r\nException: "+e.getMessage());
throw e;
}
if (output.getDifferential().hasElement())
new NarrativeGenerator("", "http://hl7.org/fhir", TestingUtilities.context()).setPkp(new TestPKP()).generate(output, null);
test.output = output;
TestingUtilities.context().cacheResource(output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(TestingUtilities.resourceNameToFile("snapshot-generation", test.getId()+"-actual.xml")), output);
StructureDefinition t1 = test.expected.copy();
t1.setText(null);
StructureDefinition t2 = test.output.copy();
t2.setText(null);
Assert.assertTrue("Output does not match expected", t1.equalsDeep(t2));
}
private StructureDefinition getSD(String url) throws DefinitionException, FHIRException, IOException {
StructureDefinition sd = context.snapshots.get(url);
if (sd == null)
sd = findContainedProfile(url);
StructureDefinition sd = context.getByUrl(url);
if (sd == null)
sd = TestingUtilities.context().fetchResource(StructureDefinition.class, url);
if (!sd.hasSnapshot()) {
StructureDefinition base = getSD(sd.getBaseDefinition());
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), messages , new TestPKP());
pu.setNewSlicingProcessing(true);
List<String> errors = new ArrayList<String>();
pu.sortDifferential(base, sd, url, errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
pu.setIds(sd, false);
pu.generateSnapshot(base, sd, sd.getUrl(), "http://test.org/profile", sd.getName());
}
return sd;
}
private StructureDefinition findContainedProfile(String url) throws DefinitionException, FHIRException, IOException {
for (Resource r : context.tests.getContained()) {
if (r instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) r;
if (sd.getUrl().equals(url)) {
StructureDefinition p = sd.copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), null, null);
pu.setIds(p, false);
List<String> errors = new ArrayList<String>();
pu.sortDifferential(getSD(p.getBaseDefinition()), p, url, errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
pu.generateSnapshot(getSD(p.getBaseDefinition()), p, p.getUrl(), "http://hl7.org/fhir/R4", p.getName());
debugSaveResource(p);
return p;
}
}
}
return null;
}
private void resolveFixtures() {
if (context.fixtures == null) {
context.fixtures = new HashMap<String, Resource>();
for (TestScriptFixtureComponent fd : context.tests.getFixture()) {
Resource r = TestingUtilities.context().fetchResource(Resource.class, fd.getResource().getReference());
context.fixtures.put(fd.getId(), r);
}
}
}
}

View File

@ -526,7 +526,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
pIn.addParameter().setName("coding").setValue(code);
if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
setTerminologyOptions(options, pIn);
if (options != null)
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId());
@ -566,7 +567,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
try {
Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code);
setTerminologyOptions(options, pIn);
if (options != null)
setTerminologyOptions(options, pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());

View File

@ -1,5 +1,8 @@
package org.hl7.fhir.r5.formats;
import java.io.FileNotFoundException;
import java.io.IOException;
/*-
* #%L
* org.hl7.fhir.r5
@ -56,6 +59,8 @@ import java.net.URI;
import org.apache.commons.codec.binary.Base64;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.utilities.TextFile;
public abstract class FormatUtilities {
public static final String ID_REGEX = "[A-Za-z0-9\\-\\.]{1,64}";
@ -146,5 +151,12 @@ public abstract class FormatUtilities {
return Integer.MAX_VALUE;
}
public static Resource loadFile(String path) throws FileNotFoundException, IOException, FHIRException {
byte[] src = TextFile.fileToBytes(path);
FhirFormat fmt = determineFormat(src);
ParserBase parser = makeParser(fmt);
return parser.parse(src);
}
}

View File

@ -295,9 +295,19 @@ public abstract class XmlParserBase extends ParserBase implements IParser {
}
protected void unknownContent(XmlPullParser xpp) throws FHIRFormatError {
protected void unknownContent(XmlPullParser xpp) throws FHIRFormatError, XmlPullParserException, IOException {
if (!isAllowUnknownContent())
throw new FHIRFormatError("Unknown Content "+xpp.getName()+" @ "+pathForLocation(xpp));
// otherwise, read over whatever element this is
int count = 1;
do {
xpp.next();
if (xpp.getEventType() == XmlPullParser.END_TAG)
count--;
if (xpp.getEventType() == XmlPullParser.START_TAG)
count++;
} while (count > 0);
xpp.next();
}
protected XhtmlNode parseXhtml(XmlPullParser xpp) throws XmlPullParserException, IOException, FHIRFormatError {

View File

@ -1,240 +0,0 @@
package org.hl7.fhir.r4.validation;
/*-
* #%L
* org.hl7.fhir.validation
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.*;
import java.util.stream.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Questionnaire.*;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
/**
* Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse.
* Ignores possible modifierExtensions and extensions.
*
*/
public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
public static final String LINKID_ELEMENT = "linkId";
public static final String ITEM_ELEMENT = "item";
public static final String ANSWER_ELEMENT = "answer";
@Override
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem, Element questionnaireResponse) {
if (!questionnaireItem.hasEnableWhen()) {
return true;
}
List<EnableWhenResult> evaluationResults = questionnaireItem.getEnableWhen()
.stream()
.map(enableCondition -> evaluateCondition(enableCondition, questionnaireResponse,
questionnaireItem.getLinkId()))
.collect(Collectors.toList());
return checkConditionResults(evaluationResults, questionnaireItem);
}
public boolean checkConditionResults(List<EnableWhenResult> evaluationResults,
QuestionnaireItemComponent questionnaireItem) {
if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ANY){
return evaluationResults.stream().anyMatch(EnableWhenResult::isEnabled);
} if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ALL){
return evaluationResults.stream().allMatch(EnableWhenResult::isEnabled);
}
//TODO: Throw exception? enableBehavior is mandatory when there are multiple conditions
return true;
}
protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition,
Element questionnaireResponse, String linkId) {
//TODO: Fix EnableWhenResult stuff
List<Element> answerItems = findQuestionAnswers(questionnaireResponse,
enableCondition.getQuestion());
QuestionnaireItemOperator operator = enableCondition.getOperator();
if (operator == QuestionnaireItemOperator.EXISTS){
Type answer = enableCondition.getAnswer();
if (!(answer instanceof BooleanType)){
throw new UnprocessableEntityException("Exists-operator requires answerBoolean");
}
return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(),
linkId, enableCondition, questionnaireResponse);
}
boolean result = answerItems
.stream()
.anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer(), enableCondition.getOperator()));
return new EnableWhenResult(result, linkId, enableCondition, questionnaireResponse);
}
private Type convertToType(Element element) throws FHIRException {
if (element.fhirType().equals("BackboneElement")) {
return null;
}
Type b = new Factory().create(element.fhirType());
if (b instanceof PrimitiveType) {
((PrimitiveType<?>) b).setValueAsString(element.primitiveValue());
} else {
for (Element child : element.getChildren()) {
if (!isExtension(child)) {
b.setProperty(child.getName(), convertToType(child));
}
}
}
return b;
}
private boolean isExtension(Element element) {
return "Extension".equals(element.fhirType());
}
protected boolean evaluateAnswer(Element answer, Type expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
Type actualAnswer;
if (isExtension(answer)) {
return false;
}
try {
actualAnswer = convertToType(answer);
if (actualAnswer == null) {
return false;
}
} catch (FHIRException e) {
throw new UnprocessableEntityException("Unexpected answer type", e);
}
if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) {
throw new UnprocessableEntityException("Expected answer and actual answer have incompatible types");
}
if (expectedAnswer instanceof Coding) {
return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator);
} else if ((expectedAnswer instanceof PrimitiveType)) {
return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator);
} else if (expectedAnswer instanceof Quantity) {
return compareQuantityAnswer((Quantity)actualAnswer, (Quantity)expectedAnswer, questionnaireItemOperator);
}
// TODO: Attachment, reference?
throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass());
}
private boolean compareQuantityAnswer(Quantity actualAnswer, Quantity expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
return compareComparable(actualAnswer.getValue(), expectedAnswer.getValue(), questionnaireItemOperator);
}
private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
if (actualAnswer.getValue() instanceof Comparable){
return compareComparable((Comparable<?>)actualAnswer.getValue(), (Comparable<?>) expectedAnswer.getValue(), questionnaireItemOperator);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return actualAnswer.equalsShallow(expectedAnswer);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return !actualAnswer.equalsShallow(expectedAnswer);
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private boolean compareComparable(Comparable actual, Comparable expected,
QuestionnaireItemOperator questionnaireItemOperator) {
int result = actual.compareTo(expected);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result != 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_OR_EQUAL){
return result >= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_OR_EQUAL){
return result <= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_THAN){
return result < 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){
return result > 0;
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
/**
* Recursively look for answers to questions with the given link id
*/
private List<Element> findQuestionAnswers(Element questionnaireResponse, String question) {
List<Element> retVal = new ArrayList<>();
List<Element> items = questionnaireResponse.getChildren(ITEM_ELEMENT);
for (Element next : items) {
if (hasLinkId(next, question)) {
List<Element> answers = extractAnswer(next);
retVal.addAll(answers);
}
retVal.addAll(findQuestionAnswers(next, question));
}
return retVal;
}
private List<Element> extractAnswer(Element item) {
return item.getChildrenByName(ANSWER_ELEMENT)
.stream()
.flatMap(c -> c.getChildren().stream())
.collect(Collectors.toList());
}
private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == true;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result == false;
}
throw new UnprocessableEntityException("Bad operator for Coding comparison");
}
private boolean compareCodes(Coding expectedCoding, Coding value) {
if (expectedCoding.hasCode() != value.hasCode()) {
return false;
}
if (expectedCoding.hasCode()) {
return expectedCoding.getCode().equals(value.getCode());
}
return true;
}
private boolean compareSystems(Coding expectedCoding, Coding value) {
if (expectedCoding.hasSystem() && !value.hasSystem()) {
return false;
}
if (expectedCoding.hasSystem()) {
return expectedCoding.getSystem().equals(value.getSystem());
}
return true;
}
private boolean hasLinkId(Element item, String linkId) {
Element linkIdChild = item.getNamedChild(LINKID_ELEMENT);
if (linkIdChild != null && linkIdChild.getValue().equals(linkId)){
return true;
}
return false;
}
}

View File

@ -0,0 +1,357 @@
package org.hl7.fhir.r4.validation;
/*-
* #%L
* org.hl7.fhir.validation
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.*;
import java.util.stream.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Questionnaire.*;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
/**
* Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse.
* Ignores possible modifierExtensions and extensions.
*
*/
public class EnableWhenEvaluator {
public static final String LINKID_ELEMENT = "linkId";
public static final String ITEM_ELEMENT = "item";
public static final String ANSWER_ELEMENT = "answer";
public static class QuestionnaireAnswerPair {
private QuestionnaireItemComponent q;
private Element a;
public QuestionnaireAnswerPair(QuestionnaireItemComponent q, Element a) {
super();
this.q = q;
this.a = a;
}
public QuestionnaireItemComponent getQ() {
return q;
}
public Element getA() {
return a;
}
}
public static class QStack extends ArrayList<QuestionnaireAnswerPair> {
private static final long serialVersionUID = 1L;
private Questionnaire q;
private Element a;
public QStack(Questionnaire q, Element a) {
super();
this.q = q;
this.a = a;
}
public Questionnaire getQ() {
return q;
}
public Element getA() {
return a;
}
public QStack push(QuestionnaireItemComponent q, Element a) {
QStack self = new QStack(this.q, this.a);
self.addAll(this);
self.add(new QuestionnaireAnswerPair(q, a));
return self;
}
}
public static class EnableWhenResult {
private final boolean enabled;
private final QuestionnaireItemEnableWhenComponent enableWhenCondition;
/**
* Evaluation result of enableWhen condition
*
* @param enabled
* Evaluation result
* @param linkId
* LinkId of the questionnaire item
* @param enableWhenCondition
* Evaluated enableWhen condition
* @param responseItem
* item in QuestionnaireResponse
*/
public EnableWhenResult(boolean enabled, QuestionnaireItemEnableWhenComponent enableWhenCondition) {
this.enabled = enabled;
this.enableWhenCondition = enableWhenCondition;
}
public boolean isEnabled() {
return enabled;
}
public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() {
return enableWhenCondition;
}
}
/**
* the stack contains a set of QR items that represent the tree of the QR being validated, each tagged with the definition of the item from the Q for the QR being validated
*
* the itembeing validated is in the context of the stack. For root items, the stack is empty.
*
* The context Questionnaire and QuestionnaireResponse are always available
*
* @param questionnaireItem
* @param questionnaireResponse
* @param qstack
* @return
*/
public boolean isQuestionEnabled(QuestionnaireItemComponent qitem, QStack qstack) {
if (!qitem.hasEnableWhen()) {
return true;
}
List<EnableWhenResult> evaluationResults = qitem.getEnableWhen()
.stream()
.map(enableCondition -> evaluateCondition(enableCondition, qitem, qstack))
.collect(Collectors.toList());
return checkConditionResults(evaluationResults, qitem);
}
public boolean checkConditionResults(List<EnableWhenResult> evaluationResults, QuestionnaireItemComponent questionnaireItem) {
if ((questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ANY) || evaluationResults.size() == 1){
return evaluationResults.stream().anyMatch(EnableWhenResult::isEnabled);
} if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ALL){
return evaluationResults.stream().allMatch(EnableWhenResult::isEnabled);
}
//TODO: Throw exception? enableBehavior is mandatory when there are multiple conditions
return true;
}
protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition, QuestionnaireItemComponent qitem, QStack qstack) {
List<Element> answerItems = findQuestionAnswers(qstack, qitem, enableCondition);
QuestionnaireItemOperator operator = enableCondition.getOperator();
if (operator == QuestionnaireItemOperator.EXISTS){
Type answer = enableCondition.getAnswer();
if (!(answer instanceof BooleanType)){
throw new UnprocessableEntityException("Exists-operator requires answerBoolean");
}
return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(), enableCondition);
}
boolean result = answerItems
.stream()
.anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer(), enableCondition.getOperator()));
return new EnableWhenResult(result, enableCondition);
}
private Type convertToType(Element element) throws FHIRException {
if (element.fhirType().equals("BackboneElement")) {
return null;
}
Type b = new Factory().create(element.fhirType());
if (b instanceof PrimitiveType) {
((PrimitiveType<?>) b).setValueAsString(element.primitiveValue());
} else {
for (Element child : element.getChildren()) {
if (!isExtension(child)) {
b.setProperty(child.getName(), convertToType(child));
}
}
}
return b;
}
private boolean isExtension(Element element) {
return "Extension".equals(element.fhirType());
}
protected boolean evaluateAnswer(Element answer, Type expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
Type actualAnswer;
if (isExtension(answer)) {
return false;
}
try {
actualAnswer = convertToType(answer);
if (actualAnswer == null) {
return false;
}
} catch (FHIRException e) {
throw new UnprocessableEntityException("Unexpected answer type", e);
}
if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) {
throw new UnprocessableEntityException("Expected answer and actual answer have incompatible types");
}
if (expectedAnswer instanceof Coding) {
return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator);
} else if ((expectedAnswer instanceof PrimitiveType)) {
return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator);
} else if (expectedAnswer instanceof Quantity) {
return compareQuantityAnswer((Quantity)actualAnswer, (Quantity)expectedAnswer, questionnaireItemOperator);
}
// TODO: Attachment, reference?
throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass());
}
private boolean compareQuantityAnswer(Quantity actualAnswer, Quantity expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
return compareComparable(actualAnswer.getValue(), expectedAnswer.getValue(), questionnaireItemOperator);
}
private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
if (actualAnswer.getValue() instanceof Comparable){
return compareComparable((Comparable<?>)actualAnswer.getValue(), (Comparable<?>) expectedAnswer.getValue(), questionnaireItemOperator);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return actualAnswer.equalsShallow(expectedAnswer);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return !actualAnswer.equalsShallow(expectedAnswer);
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private boolean compareComparable(Comparable actual, Comparable expected,
QuestionnaireItemOperator questionnaireItemOperator) {
int result = actual.compareTo(expected);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result != 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_OR_EQUAL){
return result >= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_OR_EQUAL){
return result <= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_THAN){
return result < 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){
return result > 0;
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison: "+questionnaireItemOperator.toCode());
}
/**
* Recursively look for answers to questions with the given link id, working upwards given the context
*
* For discussion about this, see https://chat.fhir.org/#narrow/stream/179255-questionnaire/topic/enable-when
*
- given sourceQ - question that contains the enableWhen reference and targetQ - question that the enableWhen references in the Q and also sourceA - answer for sourceQ and targetA - answer for targetQ in the QR
- work up from sourceQ until you find the Q group that also contains targetQ - this is groupQ
- work up from sourceA until you find the QR group that matches groupQ - this is groupA
- any targetA in groupA are input for the enableWhen decision
*/
private List<Element> findQuestionAnswers(QStack qstack, QuestionnaireItemComponent sourceQ, QuestionnaireItemEnableWhenComponent ew) {
QuestionnaireItemComponent targetQ = qstack.getQ().getQuestion(ew.getQuestion());
if (targetQ != null) {
QuestionnaireItemComponent groupQ = qstack.getQ().getCommonGroup(sourceQ, targetQ);
if (groupQ == null) { // root is Q itself
return findOnItem(qstack.getA(), ew.getQuestion());
} else {
for (int i = qstack.size() - 1; i >= 0; i--) {
if (qstack.get(i).getQ() == groupQ) {
// group A
return findOnItem(qstack.get(i).getA(), ew.getQuestion());
}
}
}
}
return new ArrayList<>();
}
private List<Element> findOnItem(Element focus, String question) {
List<Element> retVal = new ArrayList<>();
List<Element> items = focus.getChildren(ITEM_ELEMENT);
for (Element item : items) {
if (hasLinkId(item, question)) {
List<Element> answers = extractAnswer(item);
retVal.addAll(answers);
}
retVal.addAll(findOnItem(item, question));
}
// In case the question with the enableWhen is a direct child of the question with
// the answer that it depends on. There is an example of this in the
// "BO_ConsDrop" question in this test case:
// https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-validation/src/test/resources/dstu3/fmc03-questionnaire.json
if (hasLinkId(focus, question)) {
List<Element> answers = extractAnswer(focus);
retVal.addAll(answers);
}
return retVal;
}
private List<Element> extractAnswer(Element item) {
return item.getChildrenByName(ANSWER_ELEMENT)
.stream()
.flatMap(c -> c.getChildren().stream())
.collect(Collectors.toList());
}
private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == true;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result == false;
}
throw new UnprocessableEntityException("Bad operator for Coding comparison");
}
private boolean compareCodes(Coding expectedCoding, Coding value) {
if (expectedCoding.hasCode() != value.hasCode()) {
return false;
}
if (expectedCoding.hasCode()) {
return expectedCoding.getCode().equals(value.getCode());
}
return true;
}
private boolean compareSystems(Coding expectedCoding, Coding value) {
if (expectedCoding.hasSystem() && !value.hasSystem()) {
return false;
}
if (expectedCoding.hasSystem()) {
return expectedCoding.getSystem().equals(value.getSystem());
}
return true;
}
private boolean hasLinkId(Element item, String linkId) {
Element linkIdChild = item.getNamedChild(LINKID_ELEMENT);
if (linkIdChild != null && linkIdChild.getValue().equals(linkId)){
return true;
}
return false;
}
}

View File

@ -1,30 +0,0 @@
package org.hl7.fhir.r4.validation;
/*-
* #%L
* org.hl7.fhir.validation
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
public interface IEnableWhenEvaluator {
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem,
Element questionnaireResponse);
}