add support for Liquid assign + Fix JSON unicode encoding and add character check in validator for illegal XML Unicode characters
This commit is contained in:
parent
7302f52879
commit
a3c32d86a0
|
@ -47,6 +47,7 @@ import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.ExpressionNodeWithOffset;
|
import org.hl7.fhir.r5.utils.FHIRPathEngine.ExpressionNodeWithOffset;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
|
||||||
|
@ -67,17 +68,27 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
|
|
||||||
private class LiquidEngineContext {
|
private class LiquidEngineContext {
|
||||||
private Object externalContext;
|
private Object externalContext;
|
||||||
private Map<String, Base> vars = new HashMap<>();
|
private Map<String, Base> loopVars = new HashMap<>();
|
||||||
|
private Map<String, Base> globalVars = new HashMap<>();
|
||||||
|
|
||||||
public LiquidEngineContext(Object externalContext) {
|
public LiquidEngineContext(Object externalContext) {
|
||||||
super();
|
super();
|
||||||
this.externalContext = externalContext;
|
this.externalContext = externalContext;
|
||||||
|
globalVars = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiquidEngineContext(Object externalContext, LiquidEngineContext existing) {
|
||||||
|
super();
|
||||||
|
this.externalContext = externalContext;
|
||||||
|
loopVars.putAll(existing.loopVars);
|
||||||
|
globalVars = existing.globalVars;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiquidEngineContext(LiquidEngineContext existing) {
|
public LiquidEngineContext(LiquidEngineContext existing) {
|
||||||
super();
|
super();
|
||||||
externalContext = existing.externalContext;
|
externalContext = existing.externalContext;
|
||||||
vars.putAll(existing.vars);
|
loopVars.putAll(existing.loopVars);
|
||||||
|
globalVars = existing.globalVars;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +196,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
String f = lexer.getCurrent();
|
String f = lexer.getCurrent();
|
||||||
LiquidFilter filter = LiquidFilter.fromCode(f);
|
LiquidFilter filter = LiquidFilter.fromCode(f);
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
lexer.error("Unknown Liquid filter '"+f+"'");
|
lexer.error(engine.getWorker().formatMessage(I18nConstants.LIQUID_UNKNOWN_FILTER, f));
|
||||||
}
|
}
|
||||||
lexer.next();
|
lexer.next();
|
||||||
if (!lexer.done() && lexer.getCurrent().equals(":")) {
|
if (!lexer.done() && lexer.getCurrent().equals(":")) {
|
||||||
|
@ -195,7 +206,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
compiled.add(new LiquidExpressionNode(filter, null));
|
compiled.add(new LiquidExpressionNode(filter, null));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lexer.error("Unexpected syntax parsing liquid statement");
|
lexer.error(engine.getWorker().formatMessage(I18nConstants.LIQUID_UNKNOWN_SYNTAX));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,6 +316,30 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class LiquidAssign extends LiquidNode {
|
||||||
|
private String varName;
|
||||||
|
private String expression;
|
||||||
|
private ExpressionNode compiled;
|
||||||
|
@Override
|
||||||
|
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
|
||||||
|
if (compiled == null) {
|
||||||
|
boolean dbl = engine.isAllowDoubleQuotes();
|
||||||
|
engine.setAllowDoubleQuotes(true);
|
||||||
|
ExpressionNodeWithOffset po = engine.parsePartial(expression, 0);
|
||||||
|
compiled = po.getNode();
|
||||||
|
engine.setAllowDoubleQuotes(dbl);
|
||||||
|
}
|
||||||
|
List<Base> list = engine.evaluate(ctxt, resource, resource, resource, compiled);
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
ctxt.globalVars.remove(varName);
|
||||||
|
} else if (list.size() == 1) {
|
||||||
|
ctxt.globalVars.put(varName, list.get(0));
|
||||||
|
} else {
|
||||||
|
throw new Error("Assign returned a list?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class LiquidFor extends LiquidNode {
|
private class LiquidFor extends LiquidNode {
|
||||||
private String varName;
|
private String varName;
|
||||||
private String condition;
|
private String condition;
|
||||||
|
@ -343,7 +378,10 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
if (limit >= 0 && i == limit) {
|
if (limit >= 0 && i == limit) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
lctxt.vars.put(varName, o);
|
if (lctxt.globalVars.containsKey(varName)) {
|
||||||
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_VARIABLE_ALREADY_ASSIGNED, varName));
|
||||||
|
}
|
||||||
|
lctxt.loopVars.put(varName, o);
|
||||||
boolean wantBreak = false;
|
boolean wantBreak = false;
|
||||||
for (LiquidNode n : body) {
|
for (LiquidNode n : body) {
|
||||||
try {
|
try {
|
||||||
|
@ -372,7 +410,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
} else if (cnt.startsWith("limit")) {
|
} else if (cnt.startsWith("limit")) {
|
||||||
cnt = cnt.substring(5).trim();
|
cnt = cnt.substring(5).trim();
|
||||||
if (!cnt.startsWith(":")) {
|
if (!cnt.startsWith(":")) {
|
||||||
throw new FHIRException("Exception evaluating "+src+": limit is not followed by ':'");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_COLON, src));
|
||||||
}
|
}
|
||||||
cnt = cnt.substring(1).trim();
|
cnt = cnt.substring(1).trim();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -380,14 +418,14 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
throw new FHIRException("Exception evaluating "+src+": limit is not followed by a number");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_NUMBER, src));
|
||||||
}
|
}
|
||||||
limit = Integer.parseInt(cnt.substring(0, i));
|
limit = Integer.parseInt(cnt.substring(0, i));
|
||||||
cnt = cnt.substring(i);
|
cnt = cnt.substring(i);
|
||||||
} else if (cnt.startsWith("offset")) {
|
} else if (cnt.startsWith("offset")) {
|
||||||
cnt = cnt.substring(6).trim();
|
cnt = cnt.substring(6).trim();
|
||||||
if (!cnt.startsWith(":")) {
|
if (!cnt.startsWith(":")) {
|
||||||
throw new FHIRException("Exception evaluating "+src+": limit is not followed by ':'");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_COLON, src));
|
||||||
}
|
}
|
||||||
cnt = cnt.substring(1).trim();
|
cnt = cnt.substring(1).trim();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -395,12 +433,12 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
throw new FHIRException("Exception evaluating "+src+": limit is not followed by a number");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_NUMBER, src));
|
||||||
}
|
}
|
||||||
offset = Integer.parseInt(cnt.substring(0, i));
|
offset = Integer.parseInt(cnt.substring(0, i));
|
||||||
cnt = cnt.substring(i);
|
cnt = cnt.substring(i);
|
||||||
} else {
|
} else {
|
||||||
throw new FHIRException("Exception evaluating "+src+": unexpected content at "+cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_UNEXPECTED, cnt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,9 +453,9 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
String src = includeResolver.fetchInclude(LiquidEngine.this, page);
|
String src = includeResolver.fetchInclude(LiquidEngine.this, page);
|
||||||
LiquidParser parser = new LiquidParser(src);
|
LiquidParser parser = new LiquidParser(src);
|
||||||
LiquidDocument doc = parser.parse(page);
|
LiquidDocument doc = parser.parse(page);
|
||||||
LiquidEngineContext nctxt = new LiquidEngineContext(ctxt.externalContext);
|
LiquidEngineContext nctxt = new LiquidEngineContext(ctxt.externalContext, ctxt);
|
||||||
Tuple incl = new Tuple();
|
Tuple incl = new Tuple();
|
||||||
nctxt.vars.put("include", incl);
|
nctxt.loopVars.put("include", incl);
|
||||||
for (String s : params.keySet()) {
|
for (String s : params.keySet()) {
|
||||||
incl.addProperty(s, engine.evaluate(ctxt, resource, resource, resource, params.get(s)));
|
incl.addProperty(s, engine.evaluate(ctxt, resource, resource, resource, params.get(s)));
|
||||||
}
|
}
|
||||||
|
@ -474,11 +512,11 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
cnt = "," + cnt.substring(5).trim();
|
cnt = "," + cnt.substring(5).trim();
|
||||||
while (!Utilities.noString(cnt)) {
|
while (!Utilities.noString(cnt)) {
|
||||||
if (!cnt.startsWith(",")) {
|
if (!cnt.startsWith(",")) {
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Found " + cnt.charAt(0) + " expecting ',' parsing cycle");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_EXPECTING, name, cnt.charAt(0), ','));
|
||||||
}
|
}
|
||||||
cnt = cnt.substring(1).trim();
|
cnt = cnt.substring(1).trim();
|
||||||
if (!cnt.startsWith("\"")) {
|
if (!cnt.startsWith("\"")) {
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Found " + cnt.charAt(0) + " expecting '\"' parsing cycle");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_EXPECTING, name, cnt.charAt(0), '"'));
|
||||||
}
|
}
|
||||||
cnt = cnt.substring(1);
|
cnt = cnt.substring(1);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -486,7 +524,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (i == cnt.length()) {
|
if (i == cnt.length()) {
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Found unterminated string parsing cycle");
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_UNTERMINATED, name));
|
||||||
}
|
}
|
||||||
res.list.add(cnt.substring(0, i));
|
res.list.add(cnt.substring(0, i));
|
||||||
cnt = cnt.substring(i + 1).trim();
|
cnt = cnt.substring(i + 1).trim();
|
||||||
|
@ -518,8 +556,10 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
list.add(parseCycle(cnt));
|
list.add(parseCycle(cnt));
|
||||||
else if (cnt.startsWith("include "))
|
else if (cnt.startsWith("include "))
|
||||||
list.add(parseInclude(cnt.substring(7).trim()));
|
list.add(parseInclude(cnt.substring(7).trim()));
|
||||||
|
else if (cnt.startsWith("assign "))
|
||||||
|
list.add(parseAssign(cnt.substring(6).trim()));
|
||||||
else
|
else
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Unknown flow control statement " + cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_UNKNOWN_FLOW_STMT,name, cnt));
|
||||||
} else { // next2() == '{'
|
} else { // next2() == '{'
|
||||||
list.add(parseStatement());
|
list.add(parseStatement());
|
||||||
}
|
}
|
||||||
|
@ -533,7 +573,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
n.closeUp();
|
n.closeUp();
|
||||||
if (terminators.length > 0)
|
if (terminators.length > 0)
|
||||||
if (!isTerminator(close, terminators))
|
if (!isTerminator(close, terminators))
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Found end of script looking for " + terminators);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_UNKNOWN_NOEND, name, terminators));
|
||||||
return close;
|
return close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +617,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
while (i < cnt.length() && !Character.isWhitespace(cnt.charAt(i)))
|
while (i < cnt.length() && !Character.isWhitespace(cnt.charAt(i)))
|
||||||
i++;
|
i++;
|
||||||
if (i == cnt.length() || i == 0)
|
if (i == cnt.length() || i == 0)
|
||||||
throw new FHIRException("Script " + name + ": Error reading include: " + cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_INCLUDE, name + ": Error reading include: " + cnt));
|
||||||
LiquidInclude res = new LiquidInclude();
|
LiquidInclude res = new LiquidInclude();
|
||||||
res.page = cnt.substring(0, i);
|
res.page = cnt.substring(0, i);
|
||||||
while (i < cnt.length() && Character.isWhitespace(cnt.charAt(i)))
|
while (i < cnt.length() && Character.isWhitespace(cnt.charAt(i)))
|
||||||
|
@ -587,10 +627,10 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
while (i < cnt.length() && cnt.charAt(i) != '=')
|
while (i < cnt.length() && cnt.charAt(i) != '=')
|
||||||
i++;
|
i++;
|
||||||
if (i >= cnt.length() || j == i)
|
if (i >= cnt.length() || j == i)
|
||||||
throw new FHIRException("Script " + name + ": Error reading include: " + cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_INCLUDE, name, cnt));
|
||||||
String n = cnt.substring(j, i);
|
String n = cnt.substring(j, i);
|
||||||
if (res.params.containsKey(n))
|
if (res.params.containsKey(n))
|
||||||
throw new FHIRException("Script " + name + ": Error reading include: " + cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_INCLUDE, name, cnt));
|
||||||
i++;
|
i++;
|
||||||
ExpressionNodeWithOffset t = engine.parsePartial(cnt, i);
|
ExpressionNodeWithOffset t = engine.parsePartial(cnt, i);
|
||||||
i = t.getOffset();
|
i = t.getOffset();
|
||||||
|
@ -607,13 +647,16 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
i++;
|
i++;
|
||||||
LiquidFor res = new LiquidFor();
|
LiquidFor res = new LiquidFor();
|
||||||
res.varName = cnt.substring(0, i);
|
res.varName = cnt.substring(0, i);
|
||||||
|
if ("include".equals(res.varName)) {
|
||||||
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_VARIABLE_ILLEGAL, res.varName));
|
||||||
|
}
|
||||||
while (Character.isWhitespace(cnt.charAt(i)))
|
while (Character.isWhitespace(cnt.charAt(i)))
|
||||||
i++;
|
i++;
|
||||||
int j = i;
|
int j = i;
|
||||||
while (!Character.isWhitespace(cnt.charAt(i)))
|
while (!Character.isWhitespace(cnt.charAt(i)))
|
||||||
i++;
|
i++;
|
||||||
if (!"in".equals(cnt.substring(j, i)))
|
if (!"in".equals(cnt.substring(j, i)))
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Error reading loop: " + cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_LOOP, name, cnt));
|
||||||
res.condition = cnt.substring(i).trim();
|
res.condition = cnt.substring(i).trim();
|
||||||
parseList(res.body, false, new String[] { "endloop" });
|
parseList(res.body, false, new String[] { "endloop" });
|
||||||
return res;
|
return res;
|
||||||
|
@ -625,13 +668,16 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
i++;
|
i++;
|
||||||
LiquidFor res = new LiquidFor();
|
LiquidFor res = new LiquidFor();
|
||||||
res.varName = cnt.substring(0, i);
|
res.varName = cnt.substring(0, i);
|
||||||
|
if ("include".equals(res.varName)) {
|
||||||
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_VARIABLE_ILLEGAL, res.varName));
|
||||||
|
}
|
||||||
while (Character.isWhitespace(cnt.charAt(i)))
|
while (Character.isWhitespace(cnt.charAt(i)))
|
||||||
i++;
|
i++;
|
||||||
int j = i;
|
int j = i;
|
||||||
while (!Character.isWhitespace(cnt.charAt(i)))
|
while (!Character.isWhitespace(cnt.charAt(i)))
|
||||||
i++;
|
i++;
|
||||||
if (!"in".equals(cnt.substring(j, i)))
|
if (!"in".equals(cnt.substring(j, i)))
|
||||||
throw new FHIRException("Script " + name + ": Script " + name + ": Error reading loop: " + cnt);
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_LOOP, name, cnt));
|
||||||
res.condition = cnt.substring(i).trim();
|
res.condition = cnt.substring(i).trim();
|
||||||
String term = parseList(res.body, true, new String[] { "endfor", "else" });
|
String term = parseList(res.body, true, new String[] { "endfor", "else" });
|
||||||
if ("else".equals(term)) {
|
if ("else".equals(term)) {
|
||||||
|
@ -640,6 +686,20 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LiquidNode parseAssign(String cnt) throws FHIRException {
|
||||||
|
int i = 0;
|
||||||
|
while (!Character.isWhitespace(cnt.charAt(i)))
|
||||||
|
i++;
|
||||||
|
LiquidAssign res = new LiquidAssign();
|
||||||
|
res.varName = cnt.substring(0, i);
|
||||||
|
while (Character.isWhitespace(cnt.charAt(i)))
|
||||||
|
i++;
|
||||||
|
int j = i;
|
||||||
|
while (!Character.isWhitespace(cnt.charAt(i)))
|
||||||
|
i++;
|
||||||
|
res.expression = cnt.substring(i).trim();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private String parseTag(char ch) throws FHIRException {
|
private String parseTag(char ch) throws FHIRException {
|
||||||
grab();
|
grab();
|
||||||
|
@ -649,7 +709,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
b.append(grab());
|
b.append(grab());
|
||||||
}
|
}
|
||||||
if (!(next1() == '%' && next2() == '}'))
|
if (!(next1() == '%' && next2() == '}'))
|
||||||
throw new FHIRException("Script " + name + ": Unterminated Liquid statement {% " + b.toString());
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_NOTERM, name, "{% " + b.toString()));
|
||||||
grab();
|
grab();
|
||||||
grab();
|
grab();
|
||||||
return b.toString().trim();
|
return b.toString().trim();
|
||||||
|
@ -663,7 +723,7 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
b.append(grab());
|
b.append(grab());
|
||||||
}
|
}
|
||||||
if (!(next1() == '}' && next2() == '}'))
|
if (!(next1() == '}' && next2() == '}'))
|
||||||
throw new FHIRException("Script " + name + ": Unterminated Liquid statement {{ " + b.toString());
|
throw new FHIRException(engine.getWorker().formatMessage(I18nConstants.LIQUID_SYNTAX_NOTERM, name, "{{ " + b.toString()));
|
||||||
grab();
|
grab();
|
||||||
grab();
|
grab();
|
||||||
LiquidStatement res = new LiquidStatement();
|
LiquidStatement res = new LiquidStatement();
|
||||||
|
@ -676,8 +736,10 @@ public class LiquidEngine implements IEvaluationContext {
|
||||||
@Override
|
@Override
|
||||||
public List<Base> resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
|
public List<Base> resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
|
||||||
LiquidEngineContext ctxt = (LiquidEngineContext) appContext;
|
LiquidEngineContext ctxt = (LiquidEngineContext) appContext;
|
||||||
if (ctxt.vars.containsKey(name))
|
if (ctxt.loopVars.containsKey(name))
|
||||||
return new ArrayList<Base>(Arrays.asList(ctxt.vars.get(name)));
|
return new ArrayList<Base>(Arrays.asList(ctxt.loopVars.get(name)));
|
||||||
|
if (ctxt.globalVars.containsKey(name))
|
||||||
|
return new ArrayList<Base>(Arrays.asList(ctxt.globalVars.get(name)));
|
||||||
if (externalHostServices == null)
|
if (externalHostServices == null)
|
||||||
return new ArrayList<Base>();
|
return new ArrayList<Base>();
|
||||||
return externalHostServices.resolveConstant(ctxt.externalContext, name, beforeContext);
|
return externalHostServices.resolveConstant(ctxt.externalContext, name, beforeContext);
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.hl7.fhir.r5.formats;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class UnicodeCharacterTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnicodeXml() throws FHIRFormatError, IOException {
|
||||||
|
XmlParser xml = new XmlParser();
|
||||||
|
xml.setOutputStyle(OutputStyle.PRETTY);
|
||||||
|
Parameters p = (Parameters) xml.parse(TestingUtilities.loadTestResource("r5", "unicode-problem.xml"));
|
||||||
|
Assertions.assertEquals("invalid: \u0013, not invalid: \r", p.getParameterFirstRep().getValue().primitiveValue());
|
||||||
|
FileOutputStream o = new FileOutputStream(Utilities.path("[tmp]", "unicode-problem.xml"));
|
||||||
|
xml.compose(o, p);
|
||||||
|
o.close();
|
||||||
|
p = (Parameters) xml.parse(new FileInputStream(Utilities.path("[tmp]", "unicode-problem.xml")));
|
||||||
|
Assertions.assertEquals("invalid: \u0013, not invalid: \r", p.getParameterFirstRep().getValue().primitiveValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnicodeJson() throws FHIRFormatError, IOException {
|
||||||
|
JsonParser json = new JsonParser();
|
||||||
|
json.setOutputStyle(OutputStyle.PRETTY);
|
||||||
|
Parameters p = (Parameters) json.parse(TestingUtilities.loadTestResource("r5", "unicode-problem.json"));
|
||||||
|
Assertions.assertEquals("invalid: \u0013, not invalid: \r", p.getParameterFirstRep().getValue().primitiveValue());
|
||||||
|
FileOutputStream o = new FileOutputStream(Utilities.path("[tmp]", "unicode-problem.json"));
|
||||||
|
json.compose(o, p);
|
||||||
|
o.close();
|
||||||
|
p = (Parameters) json.parse(new FileInputStream(Utilities.path("[tmp]", "unicode-problem.json")));
|
||||||
|
Assertions.assertEquals("invalid: \u0013, not invalid: \r", p.getParameterFirstRep().getValue().primitiveValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -984,7 +984,7 @@ public class Utilities {
|
||||||
else if (isWhitespace(c)) {
|
else if (isWhitespace(c)) {
|
||||||
b.append("\\u"+Utilities.padLeft(Integer.toHexString(c), '0', 4));
|
b.append("\\u"+Utilities.padLeft(Integer.toHexString(c), '0', 4));
|
||||||
} else if (((int) c) < 32)
|
} else if (((int) c) < 32)
|
||||||
b.append("\\u" + Utilities.padLeft(String.valueOf((int) c), '0', 4));
|
b.append("\\u" + Utilities.padLeft(Integer.toHexString(c), '0', 4));
|
||||||
else
|
else
|
||||||
b.append(c);
|
b.append(c);
|
||||||
}
|
}
|
||||||
|
|
|
@ -926,6 +926,24 @@ public class I18nConstants {
|
||||||
public static final String VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = "VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED";
|
public static final String VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = "VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED";
|
||||||
public static final String VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = "VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED";
|
public static final String VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = "VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED";
|
||||||
public static final String CS_SCT_IPS_NOT_IPS = "CS_SCT_IPS_NOT_IPS";
|
public static final String CS_SCT_IPS_NOT_IPS = "CS_SCT_IPS_NOT_IPS";
|
||||||
|
public static final String UNICODE_XML_BAD_CHARS = "UNICODE_XML_BAD_CHARS";
|
||||||
|
public static final String LIQUID_UNKNOWN_FILTER = "LIQUID_UNKNOWN_FILTER";
|
||||||
|
public static final String LIQUID_UNKNOWN_SYNTAX = "LIQUID_UNKNOWN_SYNTAX";
|
||||||
|
public static final String LIQUID_SYNTAX_EXPECTING = "LIQUID_SYNTAX_EXPECTING";
|
||||||
|
public static final String LIQUID_SYNTAX_UNTERMINATED = "LIQUID_SYNTAX_UNTERMINATED";
|
||||||
|
public static final String LIQUID_UNKNOWN_FLOW_STMT = "LIQUID_UNKNOWN_FLOW_STMT";
|
||||||
|
public static final String LIQUID_UNKNOWN_NOEND = "LIQUID_UNKNOWN_NOEND";
|
||||||
|
public static final String LIQUID_SYNTAX_INCLUDE = "LIQUID_SYNTAX_INCLUDE";
|
||||||
|
public static final String LIQUID_SYNTAX_LOOP = "LIQUID_SYNTAX_LOOP";
|
||||||
|
public static final String LIQUID_SYNTAX_NOTERM = "LIQUID_SYNTAX_NOTERM";
|
||||||
|
public static final String LIQUID_UNKNOWN_NOTERM = "LIQUID_UNKNOWN_NOTERM";
|
||||||
|
public static final String LIQUID_SYNTAX_COLON = "LIQUID_SYNTAX_COLON";
|
||||||
|
public static final String LIQUID_SYNTAX_NUMBER = "LIQUID_SYNTAX_NUMBER";
|
||||||
|
public static final String LIQUID_SYNTAX_UNEXPECTED = "LIQUID_SYNTAX_UNEXPECTED";
|
||||||
|
public static final String LIQUID_VARIABLE_ALREADY_ASSIGNED = "LIQUID_VARIABLE_ALREADY_ASSIGNED";
|
||||||
|
public static final String LIQUID_VARIABLE_ILLEGAL = "LIQUID_VARIABLE_ILLEGAL";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -981,3 +981,21 @@ SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the typ
|
||||||
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
|
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
|
||||||
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended
|
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended
|
||||||
CS_SCT_IPS_NOT_IPS = The Snomed CT code {0} ({1}) is not a member of the IPS free set
|
CS_SCT_IPS_NOT_IPS = The Snomed CT code {0} ({1}) is not a member of the IPS free set
|
||||||
|
UNICODE_XML_BAD_CHARS_one = This content includes the character {1} (hex value). This character is illegal in the XML version of FHIR, and there is generally no valid use for such characters
|
||||||
|
UNICODE_XML_BAD_CHARS_other = This content includes the characters {1} (hex values). These characters are illegal in the XML version of FHIR, and there is generally no valid use for such characters
|
||||||
|
LIQUID_UNKNOWN_FILTER = Unknown Liquid filter '''{0}''
|
||||||
|
LIQUID_UNKNOWN_SYNTAX = Unexpected syntax parsing liquid statement
|
||||||
|
LIQUID_SYNTAX_EXPECTING = Script {0}: Found ''{1}'' expecting ''{2}'' parsing cycle
|
||||||
|
LIQUID_SYNTAX_UNTERMINATED = Script {0}: Found unterminated string parsing cycle
|
||||||
|
LIQUID_UNKNOWN_FLOW_STMT = Script {0}: Unknown flow control statement ''{1}''
|
||||||
|
LIQUID_UNKNOWN_NOEND = Script {0}: Found end of script looking for {1}
|
||||||
|
LIQUID_SYNTAX_INCLUDE, = Script {0}: Error reading include: {1}
|
||||||
|
LIQUID_SYNTAX_LOOP = Script {0}: Error reading loop: {1}
|
||||||
|
LIQUID_SYNTAX_NOTERM = Script {0}: Unterminated Liquid statement {1}
|
||||||
|
LIQUID_UNKNOWN_NOTERM = Script {0}: Unterminated Liquid statement {1}
|
||||||
|
LIQUID_SYNTAX_COLON = Exception evaluating {0}: limit is not followed by '':''
|
||||||
|
LIQUID_SYNTAX_NUMBER = Exception evaluating {0}: limit is not followed by a number
|
||||||
|
LIQUID_SYNTAX_UNEXPECTED = Exception evaluating {0}: unexpected content at {1}
|
||||||
|
LIQUID_VARIABLE_ALREADY_ASSIGNED = "Liquid Exception: The variable ''{0}'' already has an assigned value
|
||||||
|
LIQUID_VARIABLE_ILLEGAL = "Liquid Exception: The variable name ''{0}'' cannot be used
|
||||||
|
|
|
@ -2379,6 +2379,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Set<String> badChars = new HashSet<>();
|
||||||
|
for (char ch : e.primitiveValue().toCharArray()) {
|
||||||
|
if (ch < 32 && !(ch == '\r' || ch == '\n' || ch == '\t')) {
|
||||||
|
// can't get to here with xml - the parser fails if you try
|
||||||
|
badChars.add(Integer.toHexString(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warningPlural(errors, "2023-07-26", IssueType.INVALID, e.line(), e.col(), path, badChars.isEmpty(), badChars.size(), I18nConstants.UNICODE_XML_BAD_CHARS, badChars.toString());
|
||||||
}
|
}
|
||||||
String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX);
|
String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX);
|
||||||
// there's a messy history here - this extension snhould only be on the element definition itself, but for historical reasons
|
// there's a messy history here - this extension snhould only be on the element definition itself, but for historical reasons
|
||||||
|
|
Loading…
Reference in New Issue