Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core into utilities_test_cases
This commit is contained in:
commit
8a7737cac0
|
@ -309,7 +309,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
for (ElementDefinition ed : sd.getDifferential().getElement()) {
|
for (ElementDefinition ed : sd.getDifferential().getElement()) {
|
||||||
if (ed.getPath().equals("Extension.url") || ed.getPath().endsWith(".extension.url") ) {
|
if (ed.getPath().equals("Extension.url") || ed.getPath().endsWith(".extension.url") ) {
|
||||||
ed.setMin(1);
|
ed.setMin(1);
|
||||||
ed.getBase().setMin(1);
|
if (ed.hasBase()) {
|
||||||
|
ed.getBase().setMin(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ("extension".equals(ed.getSliceName())) {
|
if ("extension".equals(ed.getSliceName())) {
|
||||||
ed.setSliceName(null);
|
ed.setSliceName(null);
|
||||||
|
|
|
@ -79,6 +79,15 @@ private Map<String, Object> userData;
|
||||||
return (Integer) getUserData(name);
|
return (Integer) getUserData(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void copyUserData(Base other) {
|
||||||
|
if (other.userData != null) {
|
||||||
|
if (userData == null) {
|
||||||
|
userData = new HashMap<>();
|
||||||
|
}
|
||||||
|
userData.putAll(other.userData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasFormatComment() {
|
public boolean hasFormatComment() {
|
||||||
return (formatCommentsPre != null && !formatCommentsPre.isEmpty()) || (formatCommentsPost != null && !formatCommentsPost.isEmpty());
|
return (formatCommentsPre != null && !formatCommentsPre.isEmpty()) || (formatCommentsPost != null && !formatCommentsPost.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,10 @@ public class ExpressionNode {
|
||||||
First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length,
|
First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length,
|
||||||
Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue,
|
Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue,
|
||||||
HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo,
|
HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo,
|
||||||
|
// R3 functions
|
||||||
|
Encode, Decode, Escape, Trim, Split, Join,
|
||||||
// Local extensions to FHIRPath
|
// Local extensions to FHIRPath
|
||||||
HtmlChecks, AliasAs, Alias, fromBase64, toBase64;
|
HtmlChecks, AliasAs, Alias;
|
||||||
|
|
||||||
public static Function fromCode(String name) {
|
public static Function fromCode(String name) {
|
||||||
if (name.equals("empty")) return Function.Empty;
|
if (name.equals("empty")) return Function.Empty;
|
||||||
|
@ -134,8 +136,12 @@ public class ExpressionNode {
|
||||||
if (name.equals("aliasAs")) return Function.AliasAs;
|
if (name.equals("aliasAs")) return Function.AliasAs;
|
||||||
if (name.equals("htmlChecks")) return Function.HtmlChecks;
|
if (name.equals("htmlChecks")) return Function.HtmlChecks;
|
||||||
if (name.equals("htmlchecks")) return Function.HtmlChecks; // support change of care from R3
|
if (name.equals("htmlchecks")) return Function.HtmlChecks; // support change of care from R3
|
||||||
if (name.equals("fromBase64")) return Function.fromBase64;
|
if (name.equals("encode")) return Function.Encode;
|
||||||
if (name.equals("toBase64")) return Function.toBase64;
|
if (name.equals("decode")) return Function.Decode;
|
||||||
|
if (name.equals("escape")) return Function.Escape;
|
||||||
|
if (name.equals("trim")) return Function.Trim;
|
||||||
|
if (name.equals("split")) return Function.Split;
|
||||||
|
if (name.equals("join")) return Function.Join;
|
||||||
if (name.equals("ofType")) return Function.OfType;
|
if (name.equals("ofType")) return Function.OfType;
|
||||||
if (name.equals("type")) return Function.Type;
|
if (name.equals("type")) return Function.Type;
|
||||||
if (name.equals("toInteger")) return Function.ToInteger;
|
if (name.equals("toInteger")) return Function.ToInteger;
|
||||||
|
@ -212,8 +218,12 @@ public class ExpressionNode {
|
||||||
case HasValue : return "hasValue";
|
case HasValue : return "hasValue";
|
||||||
case Alias : return "alias";
|
case Alias : return "alias";
|
||||||
case AliasAs : return "aliasAs";
|
case AliasAs : return "aliasAs";
|
||||||
case fromBase64 : return "fromBase64";
|
case Encode : return "encode";
|
||||||
case toBase64 : return "toBase64";
|
case Decode : return "decode";
|
||||||
|
case Escape : return "escape";
|
||||||
|
case Trim : return "trim";
|
||||||
|
case Split : return "split";
|
||||||
|
case Join : return "join";
|
||||||
case HtmlChecks : return "htmlChecks";
|
case HtmlChecks : return "htmlChecks";
|
||||||
case OfType : return "ofType";
|
case OfType : return "ofType";
|
||||||
case Type : return "type";
|
case Type : return "type";
|
||||||
|
|
|
@ -69,8 +69,12 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Description & Constraints"), translate("sd.hint", "Additional information about the item"), null, 0));
|
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Description & Constraints"), translate("sd.hint", "Additional information about the item"), null, 0));
|
||||||
|
|
||||||
boolean hasExt = false;
|
boolean hasExt = false;
|
||||||
for (QuestionnaireItemComponent i : q.getItem()) {
|
if (!q.hasItem()) {
|
||||||
hasExt = renderTreeItem(gen, model.getRows(), q, i) || hasExt;
|
gen.emptyRow(model, 6);
|
||||||
|
} else {
|
||||||
|
for (QuestionnaireItemComponent i : q.getItem()) {
|
||||||
|
hasExt = renderTreeItem(gen, model.getRows(), q, i) || hasExt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null);
|
XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null);
|
||||||
x.getChildNodes().add(xn);
|
x.getChildNodes().add(xn);
|
||||||
|
@ -83,7 +87,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
boolean hasExt = false;
|
boolean hasExt = false;
|
||||||
|
|
||||||
r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay());
|
r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay());
|
||||||
r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"-definitions.html#extension."+i.getLinkId(), i.getLinkId(), null, null));
|
r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.getLinkId(), i.getLinkId(), null, null));
|
||||||
String txt = (i.hasPrefix() ? i.getPrefix() + ". " : "") + i.getText();
|
String txt = (i.hasPrefix() ? i.getPrefix() + ". " : "") + i.getText();
|
||||||
r.getCells().add(gen.new Cell(null, null, txt, null, null));
|
r.getCells().add(gen.new Cell(null, null, txt, null, null));
|
||||||
r.getCells().add(gen.new Cell(null, null, (i.getRequired() ? "1" : "0")+".."+(i.getRepeats() ? "*" : "1"), null, null));
|
r.getCells().add(gen.new Cell(null, null, (i.getRequired() ? "1" : "0")+".."+(i.getRepeats() ? "*" : "1"), null, null));
|
||||||
|
@ -93,28 +97,28 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
Cell flags = gen.new Cell();
|
Cell flags = gen.new Cell();
|
||||||
r.getCells().add(flags);
|
r.getCells().add(flags);
|
||||||
if (i.getReadOnly()) {
|
if (i.getReadOnly()) {
|
||||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-readonly.png"))));
|
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-readonly.png"))));
|
||||||
}
|
}
|
||||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||||
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-subject.png"))));
|
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"))));
|
||||||
}
|
}
|
||||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
|
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
|
||||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-hidden.png"))));
|
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"))));
|
||||||
}
|
}
|
||||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
|
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
|
||||||
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-optional.png"))));
|
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"))));
|
||||||
}
|
}
|
||||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||||
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-observation.png"))));
|
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"))));
|
||||||
}
|
}
|
||||||
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation")) {
|
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation")) {
|
||||||
String code = ToolingExtensions.readStringExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation");
|
String code = ToolingExtensions.readStringExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation");
|
||||||
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"))));
|
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||||
}
|
}
|
||||||
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
|
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
|
||||||
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
|
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
|
||||||
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
||||||
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory", null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"))));
|
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory", null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Cell defn = gen.new Cell();
|
Cell defn = gen.new Cell();
|
||||||
|
@ -143,7 +147,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
if (vs == null) {
|
if (vs == null) {
|
||||||
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
|
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
|
||||||
} else {
|
} else {
|
||||||
defn.getPieces().add(gen.new Piece("todo", vs.present(), null));
|
defn.getPieces().add(gen.new Piece(vs.getUserString("path"), vs.present(), null));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
|
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
|
||||||
|
@ -157,7 +161,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
if (i.hasAnswerOption()) {
|
if (i.hasAnswerOption()) {
|
||||||
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
|
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
|
||||||
defn.getPieces().add(gen.new Piece(null, "Options: ", null));
|
defn.getPieces().add(gen.new Piece(null, "Options: ", null));
|
||||||
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#"+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
|
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#item."+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
|
||||||
}
|
}
|
||||||
if (i.hasInitial()) {
|
if (i.hasInitial()) {
|
||||||
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
|
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
|
||||||
|
@ -231,8 +235,12 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Description & Constraints"), translate("sd.hint", "Additional information about the item"), null, 0));
|
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Description & Constraints"), translate("sd.hint", "Additional information about the item"), null, 0));
|
||||||
|
|
||||||
boolean hasExt = false;
|
boolean hasExt = false;
|
||||||
for (QuestionnaireItemComponent i : q.getItem()) {
|
if (!q.hasItem()) {
|
||||||
hasExt = renderLogicItem(gen, model.getRows(), q, i) || hasExt;
|
gen.emptyRow(model, 2);
|
||||||
|
} else {
|
||||||
|
for (QuestionnaireItemComponent i : q.getItem()) {
|
||||||
|
hasExt = renderLogicItem(gen, model.getRows(), q, i) || hasExt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
XhtmlNode xn = gen.generate(model, context.getDestDir(), 1, null);
|
XhtmlNode xn = gen.generate(model, context.getDestDir(), 1, null);
|
||||||
x.getChildNodes().add(xn);
|
x.getChildNodes().add(xn);
|
||||||
|
@ -245,7 +253,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
boolean hasExt = false;
|
boolean hasExt = false;
|
||||||
|
|
||||||
r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay());
|
r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay());
|
||||||
r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"-definitions.html#extension."+i.getLinkId(), i.getLinkId(), null, null));
|
r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.getLinkId(), i.getLinkId(), null, null));
|
||||||
Cell defn = gen.new Cell();
|
Cell defn = gen.new Cell();
|
||||||
r.getCells().add(defn);
|
r.getCells().add(defn);
|
||||||
|
|
||||||
|
@ -272,7 +280,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
if (vs == null) {
|
if (vs == null) {
|
||||||
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
|
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
|
||||||
} else {
|
} else {
|
||||||
defn.getPieces().add(gen.new Piece("todo", vs.present(), null));
|
defn.getPieces().add(gen.new Piece(vs.getUserString("path"), vs.present(), null));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
|
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
|
||||||
|
@ -286,7 +294,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
if (i.hasAnswerOption()) {
|
if (i.hasAnswerOption()) {
|
||||||
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
|
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
|
||||||
defn.getPieces().add(gen.new Piece(null, "Options: ", null));
|
defn.getPieces().add(gen.new Piece(null, "Options: ", null));
|
||||||
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#"+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
|
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#item."+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
|
||||||
}
|
}
|
||||||
if (i.hasInitial()) {
|
if (i.hasInitial()) {
|
||||||
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
|
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
|
||||||
|
@ -459,7 +467,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
|
|
||||||
// if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation")) {
|
// if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation")) {
|
||||||
// String code = ToolingExtensions.readStringExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation");
|
// String code = ToolingExtensions.readStringExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation");
|
||||||
// flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"))));
|
// flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
@ -470,26 +478,26 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
|
|
||||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||||
hasFlag = true;
|
hasFlag = true;
|
||||||
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", "Can change the subject of the questionnaire").img(Utilities.path(context.getDestDir(), "icon-qi-subject.png"));
|
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", "Can change the subject of the questionnaire").img(Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"));
|
||||||
}
|
}
|
||||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
|
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
|
||||||
hasFlag = true;
|
hasFlag = true;
|
||||||
flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getDestDir(), "icon-qi-hidden.png"));
|
flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"));
|
||||||
d.style("background-color: #eeeeee");
|
d.style("background-color: #eeeeee");
|
||||||
}
|
}
|
||||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
|
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
|
||||||
hasFlag = true;
|
hasFlag = true;
|
||||||
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", "Is optional to display").img(Utilities.path(context.getDestDir(), "icon-qi-optional.png"));
|
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", "Is optional to display").img(Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"));
|
||||||
}
|
}
|
||||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||||
hasFlag = true;
|
hasFlag = true;
|
||||||
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", "Is linked to an observation").img(Utilities.path(context.getDestDir(), "icon-qi-observation.png"));
|
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", "Is linked to an observation").img(Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"));
|
||||||
}
|
}
|
||||||
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
|
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
|
||||||
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
|
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
|
||||||
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
||||||
hasFlag = true;
|
hasFlag = true;
|
||||||
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory", "Category: "+code).img(Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"));
|
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory", "Category: "+code).img(Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i.hasMaxLength()) {
|
if (i.hasMaxLength()) {
|
||||||
|
@ -508,7 +516,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
if (vs == null) {
|
if (vs == null) {
|
||||||
ans.tx(i.getAnswerValueSet());
|
ans.tx(i.getAnswerValueSet());
|
||||||
} else {
|
} else {
|
||||||
ans.ah("todo").tx(vs.present());
|
ans.ah(vs.getUserString("path")).tx(vs.present());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
|
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
|
||||||
|
@ -520,7 +528,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i.hasAnswerOption()) {
|
if (i.hasAnswerOption()) {
|
||||||
item(ul, "Answers", Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), context.getDefinitionsTarget()+"#"+i.getLinkId());
|
item(ul, "Answers", Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), context.getDefinitionsTarget()+"#item."+i.getLinkId());
|
||||||
}
|
}
|
||||||
if (i.hasInitial()) {
|
if (i.hasInitial()) {
|
||||||
XhtmlNode vi = item(ul, "Initial Values");
|
XhtmlNode vi = item(ul, "Initial Values");
|
||||||
|
@ -642,12 +650,12 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
private boolean renderDefinition(XhtmlNode tbl, Questionnaire q, QuestionnaireItemComponent qi, List<QuestionnaireItemComponent> parents) throws IOException {
|
private boolean renderDefinition(XhtmlNode tbl, Questionnaire q, QuestionnaireItemComponent qi, List<QuestionnaireItemComponent> parents) throws IOException {
|
||||||
boolean ext = false;
|
boolean ext = false;
|
||||||
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
|
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
|
||||||
td.an(qi.getLinkId());
|
td.an("item."+qi.getLinkId());
|
||||||
for (QuestionnaireItemComponent p : parents) {
|
for (QuestionnaireItemComponent p : parents) {
|
||||||
td.ah("#"+p.getLinkId()).img(Utilities.path(context.getDestDir(), "icon_q_item.png"));
|
td.ah("#item."+p.getLinkId()).img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"));
|
||||||
td.tx(" > ");
|
td.tx(" > ");
|
||||||
}
|
}
|
||||||
td.img(Utilities.path(context.getDestDir(), "icon_q_item.png"));
|
td.img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"));
|
||||||
td.tx(" Item ");
|
td.tx(" Item ");
|
||||||
td.b().tx(qi.getLinkId());
|
td.b().tx(qi.getLinkId());
|
||||||
|
|
||||||
|
@ -767,7 +775,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderEnableWhen(XhtmlNode x, QuestionnaireItemEnableWhenComponent ew) {
|
private void renderEnableWhen(XhtmlNode x, QuestionnaireItemEnableWhenComponent ew) {
|
||||||
x.ah("#"+ew.getQuestion()).tx(ew.getQuestion());
|
x.ah("#item."+ew.getQuestion()).tx(ew.getQuestion());
|
||||||
x.tx(" ");
|
x.tx(" ");
|
||||||
x.tx(ew.getOperator().toCode());
|
x.tx(ew.getOperator().toCode());
|
||||||
x.tx(" ");
|
x.tx(" ");
|
||||||
|
|
|
@ -1147,8 +1147,12 @@ public class FHIRPathEngine {
|
||||||
case HasValue: return checkParamCount(lexer, location, exp, 0);
|
case HasValue: return checkParamCount(lexer, location, exp, 0);
|
||||||
case Alias: return checkParamCount(lexer, location, exp, 1);
|
case Alias: return checkParamCount(lexer, location, exp, 1);
|
||||||
case AliasAs: return checkParamCount(lexer, location, exp, 1);
|
case AliasAs: return checkParamCount(lexer, location, exp, 1);
|
||||||
case fromBase64: return checkParamCount(lexer, location, exp, 0);
|
case Encode: return checkParamCount(lexer, location, exp, 1);
|
||||||
case toBase64: return checkParamCount(lexer, location, exp, 0);
|
case Decode: return checkParamCount(lexer, location, exp, 1);
|
||||||
|
case Escape: return checkParamCount(lexer, location, exp, 1);
|
||||||
|
case Trim: return checkParamCount(lexer, location, exp, 0);
|
||||||
|
case Split: return checkParamCount(lexer, location, exp, 1);
|
||||||
|
case Join: return checkParamCount(lexer, location, exp, 1);
|
||||||
case HtmlChecks: return checkParamCount(lexer, location, exp, 0);
|
case HtmlChecks: return checkParamCount(lexer, location, exp, 0);
|
||||||
case ToInteger: return checkParamCount(lexer, location, exp, 0);
|
case ToInteger: return checkParamCount(lexer, location, exp, 0);
|
||||||
case ToDecimal: return checkParamCount(lexer, location, exp, 0);
|
case ToDecimal: return checkParamCount(lexer, location, exp, 0);
|
||||||
|
@ -2646,10 +2650,23 @@ public class FHIRPathEngine {
|
||||||
return anything(CollectionStatus.SINGLETON);
|
return anything(CollectionStatus.SINGLETON);
|
||||||
case AliasAs :
|
case AliasAs :
|
||||||
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||||
return focus;
|
return focus;
|
||||||
case fromBase64:
|
case Encode:
|
||||||
|
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
||||||
case toBase64:
|
case Decode:
|
||||||
|
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||||
|
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
||||||
|
case Escape:
|
||||||
|
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||||
|
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
||||||
|
case Trim:
|
||||||
|
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
||||||
|
case Split:
|
||||||
|
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||||
|
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
||||||
|
case Join:
|
||||||
|
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
|
||||||
case ToInteger : {
|
case ToInteger : {
|
||||||
checkContextPrimitive(focus, "toInteger", true);
|
checkContextPrimitive(focus, "toInteger", true);
|
||||||
|
@ -2825,8 +2842,12 @@ public class FHIRPathEngine {
|
||||||
case AllTrue: return funcAllTrue(context, focus, exp);
|
case AllTrue: return funcAllTrue(context, focus, exp);
|
||||||
case HasValue : return funcHasValue(context, focus, exp);
|
case HasValue : return funcHasValue(context, focus, exp);
|
||||||
case AliasAs : return funcAliasAs(context, focus, exp);
|
case AliasAs : return funcAliasAs(context, focus, exp);
|
||||||
case fromBase64 : return funcFromBase64(context, focus, exp);
|
case Encode : return funcEncode(context, focus, exp);
|
||||||
case toBase64 : return funcToBase64(context, focus, exp);
|
case Decode : return funcDecode(context, focus, exp);
|
||||||
|
case Escape : return funcEscape(context, focus, exp);
|
||||||
|
case Trim : return funcTrim(context, focus, exp);
|
||||||
|
case Split : return funcSplit(context, focus, exp);
|
||||||
|
case Join : return funcJoin(context, focus, exp);
|
||||||
case Alias : return funcAlias(context, focus, exp);
|
case Alias : return funcAlias(context, focus, exp);
|
||||||
case HtmlChecks : return funcHtmlChecks(context, focus, exp);
|
case HtmlChecks : return funcHtmlChecks(context, focus, exp);
|
||||||
case ToInteger : return funcToInteger(context, focus, exp);
|
case ToInteger : return funcToInteger(context, focus, exp);
|
||||||
|
@ -2854,27 +2875,43 @@ public class FHIRPathEngine {
|
||||||
throw new Error("not Implemented yet");
|
throw new Error("not Implemented yet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Base> funcEncode(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||||
|
List<Base> result = new ArrayList<Base>();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private List<Base> funcToBase64(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
private List<Base> funcDecode(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||||
List<Base> result = new ArrayList<Base>();
|
List<Base> result = new ArrayList<Base>();
|
||||||
if (focus.size() == 1) {
|
|
||||||
String s = convertToString(focus.get(0));
|
return result;
|
||||||
result.add(new StringType(Base64.encodeBase64String(s.getBytes())));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Base> funcEscape(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||||
private List<Base> funcFromBase64(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
List<Base> result = new ArrayList<Base>();
|
||||||
List<Base> result = new ArrayList<Base>();
|
|
||||||
if (focus.size() == 1) {
|
return result;
|
||||||
String s = convertToString(focus.get(0));
|
|
||||||
result.add(new StringType(new String(Base64.decodeBase64(s))));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Base> funcTrim(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||||
|
List<Base> result = new ArrayList<Base>();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Base> funcSplit(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||||
|
List<Base> result = new ArrayList<Base>();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Base> funcJoin(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||||
|
List<Base> result = new ArrayList<Base>();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private List<Base> funcAliasAs(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
private List<Base> funcAliasAs(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||||
List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
|
List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
|
||||||
String name = nl.get(0).primitiveValue();
|
String name = nl.get(0).primitiveValue();
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package org.hl7.fhir.r5.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||||
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
|
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||||
|
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
||||||
|
import org.hl7.fhir.utilities.cache.ToolsVersion;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class XmlParserTests {
|
||||||
|
|
||||||
|
private static SimpleWorkerContext context;
|
||||||
|
private static FHIRPathEngine fp;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setUp() throws Exception {
|
||||||
|
PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
|
||||||
|
context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1"));
|
||||||
|
fp = new FHIRPathEngine(context);
|
||||||
|
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "any.xml"), "any.xml", null);
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ii.xml"), "ii.xml", null);
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cd.xml"), "cd.xml", null);
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ce.xml"), "ce.xml", null);
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ed.xml"), "ed.xml", null);
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "st.xml"), "st.xml", null);
|
||||||
|
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cda.xml"), "cda.xml", null);
|
||||||
|
for (StructureDefinition sd : context.getStructures()) {
|
||||||
|
if (!sd.hasSnapshot()) {
|
||||||
|
System.out.println("generate snapshot for " + sd.getUrl());
|
||||||
|
context.generateSnapshot(sd, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
/**
|
||||||
|
* Deserializes a simplified CDA example into the logical model and checks that
|
||||||
|
* xml deserialization works for the xsi:type
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void testXsiDeserialiserXmlParser() throws IOException {
|
||||||
|
Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example-xsi.xml"),
|
||||||
|
FhirFormat.XML);
|
||||||
|
|
||||||
|
ByteArrayOutputStream baosXml = new ByteArrayOutputStream();
|
||||||
|
Manager.compose(context, cda, baosXml, FhirFormat.XML, OutputStyle.PRETTY, null);
|
||||||
|
|
||||||
|
String cdaSerialised = baosXml.toString();
|
||||||
|
assertTrue(cdaSerialised.indexOf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"")>0);
|
||||||
|
assertTrue(cdaSerialised.indexOf("xsi:type=\"CD\"")>0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -920,4 +920,12 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
|
||||||
if (!check)
|
if (!check)
|
||||||
throw new FHIRException(message);
|
throw new FHIRException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void emptyRow(TableModel model, int cellCount) {
|
||||||
|
Row r = new Row();
|
||||||
|
model.rows.add(r);
|
||||||
|
for (int i = 0; i < cellCount; i++) {
|
||||||
|
r.getCells().add(new Cell());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue