Merge pull request #1110 from balhoff/rdf-wg-r5-with-generated

Implement R5 RDF format changes
This commit is contained in:
Grahame Grieve 2023-02-24 10:21:19 +11:00 committed by GitHub
commit 521e72a5ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1013 additions and 970 deletions

View File

@ -29,8 +29,8 @@ public class RdfParser extends RdfParserBase {
private void composeEnum(Complex parent, String parentType, String name, Enumeration<? extends Enum> value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
decorateCode(t, value);
}
@ -39,24 +39,24 @@ public class RdfParser extends RdfParserBase {
protected void composeDate(Complex parent, String parentType, String name, DateType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeDateTime(Complex parent, String parentType, String name, DateTimeType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeCode(Complex parent, String parentType, String name, CodeType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
decorateCode(t, value);
}
@ -64,136 +64,136 @@ public class RdfParser extends RdfParserBase {
protected void composeString(Complex parent, String parentType, String name, StringType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeInteger(Complex parent, String parentType, String name, IntegerType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeInteger64(Complex parent, String parentType, String name, Integer64Type value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeOid(Complex parent, String parentType, String name, OidType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeCanonical(Complex parent, String parentType, String name, CanonicalType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeUri(Complex parent, String parentType, String name, UriType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeUuid(Complex parent, String parentType, String name, UuidType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeUrl(Complex parent, String parentType, String name, UrlType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeInstant(Complex parent, String parentType, String name, InstantType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeBoolean(Complex parent, String parentType, String name, BooleanType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeBase64Binary(Complex parent, String parentType, String name, Base64BinaryType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeUnsignedInt(Complex parent, String parentType, String name, UnsignedIntType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeMarkdown(Complex parent, String parentType, String name, MarkdownType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeTime(Complex parent, String parentType, String name, TimeType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeId(Complex parent, String parentType, String name, IdType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composePositiveInt(Complex parent, String parentType, String name, PositiveIntType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}
protected void composeDecimal(Complex parent, String parentType, String name, DecimalType value, int index) {
if (value == null)
return;
Complex t = parent.predicate("fhir:"+parentType+"."+name);
t.predicate("fhir:value", ttlLiteral(value.asStringValue()));
Complex t = parent.predicate("fhir:"+name, index > -1);
t.predicate("fhir:v", ttlLiteral(value.asStringValue()), false);
composeElement(t, parentType, name, value, index);
}

View File

@ -108,7 +108,7 @@ public class JavaParserRdfGenerator extends JavaBaseGenerator {
composer.append(" if (Utilities.noString(parentType))\r\n");
composer.append(" t = parent;\r\n");
composer.append(" else {\r\n");
composer.append(" t = parent.predicate(\"fhir:\"+parentType+'.'+name);\r\n");
composer.append(" t = parent.predicate(\"fhir:\"+name,index > -1);\r\n");
composer.append(" }\r\n");
composer.append(" compose"+ti.getAncestorName()+"(t, \""+ti.getDefn().getName()+"\", name, element, index);\r\n");
if (tn.equals("Coding"))

View File

@ -333,22 +333,24 @@ public class TurtleParser extends ParserBase {
}
String subjId = genSubjectId(e);
String ontologyId = subjId.replace(">", ".ttl>");
Section ontology = ttl.section("ontology header");
ontology.triple(ontologyId, "a", "owl:Ontology");
ontology.triple(ontologyId, "owl:imports", "fhir:fhir.ttl");
if(ontologyId.startsWith("<" + FHIR_URI_BASE))
ontology.triple(ontologyId, "owl:versionIRI", ontologyId.replace(FHIR_URI_BASE, FHIR_VERSION_BASE));
Subject subject;
if (hasModifierExtension(e))
subject = section.triple(subjId, "a", "fhir:_" + e.getType());
else
subject = section.triple(subjId, "a", "fhir:" + e.getType());
subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root"), null);
Subject subject = section.triple(subjId, "a", "fhir:" + e.getType());
subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root"), null);
for (Element child : e.getChildren()) {
composeElement(section, subject, child, null);
}
for (Element child : e.getChildren()) {
composeElement(section, subject, child, null);
}
}
private boolean hasModifierExtension(Element e) {
return e.getChildren().stream().anyMatch(p -> p.getName().equals("modifierExtension"));
}
protected String getURIType(String uri) {
if(uri.startsWith("<" + FHIR_URI_BASE))
if(uri.substring(FHIR_URI_BASE.length() + 1).contains("/"))
@ -415,17 +417,18 @@ public class TurtleParser extends ParserBase {
Complex t;
if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY && parent != null && parent.getNamedChildValue("fullUrl") != null) {
String url = "<"+parent.getNamedChildValue("fullUrl")+">";
ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment);
ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment, element.getProperty().isList());
t = section.subject(url);
} else {
t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment);
t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment, element.getProperty().isList());
}
if (element.getProperty().getName().endsWith("[x]") && !element.hasValue()) {
t.linkedPredicate("a", "fhir:" + element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType()), null);
}
if (element.getSpecial() != null)
t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType()), null);
if (element.hasValue())
t.linkedPredicate("fhir:value", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType()), null);
if (element.getProperty().isList() && (!element.isResource() || element.getSpecial() == SpecialElement.CONTAINED))
t.linkedPredicate("fhir:index", Integer.toString(element.getIndex()), linkResolver == null ? null : linkResolver.resolvePage("rdf.html#index"), null);
t.linkedPredicate("fhir:v", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType()), null);
if ("Coding".equals(element.getType()))
decorateCoding(t, element, section);
@ -463,37 +466,27 @@ public class TurtleParser extends ParserBase {
private String getFormalName(Element element) {
String en = null;
if (element.getSpecial() == null) {
if (element.getProperty().getDefinition().hasBase())
en = element.getProperty().getDefinition().getBase().getPath();
}
if (element.getSpecial() == null)
en = element.getProperty().getName();
else if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY)
en = "Bundle.entry.resource";
en = "resource";
else if (element.getSpecial() == SpecialElement.BUNDLE_OUTCOME)
en = "Bundle.entry.response.outcome";
en = "outcome";
else if (element.getSpecial() == SpecialElement.PARAMETER)
en = element.getElementProperty().getDefinition().getPath();
else // CONTAINED
en = "DomainResource.contained";
en = "contained";
if (en == null)
en = element.getProperty().getDefinition().getPath();
boolean doType = false;
if (en.endsWith("[x]")) {
en = en.substring(0, en.length()-3);
doType = true;
}
if (doType || (element.getProperty().getDefinition().getType().size() > 1 && !allReference(element.getProperty().getDefinition().getType())))
en = en + Utilities.capitalize(element.getType());
return en;
}
private boolean allReference(List<TypeRefComponent> types) {
for (TypeRefComponent t : types) {
if (!t.getCode().equals("Reference"))
return false;
}
return true;
if (en == null)
en = element.getProperty().getName();
if (en.endsWith("[x]"))
en = en.substring(0, en.length()-3);
if (hasModifierExtension(element))
return "_" + en;
else
return en;
}
static public String ttlLiteral(String value, String type) {
@ -550,10 +543,14 @@ public class TurtleParser extends ParserBase {
else
t.linkedPredicate("a", "sct:" + urlescape(code), null, null);
} else if ("http://loinc.org".equals(system)) {
t.prefix("loinc", "http://loinc.org/rdf#");
t.prefix("loinc", "https://loinc.org/rdf/");
t.linkedPredicate("a", "loinc:"+urlescape(code).toUpperCase(), null, null);
} else if ("https://www.nlm.nih.gov/mesh".equals(system)) {
t.prefix("mesh", "http://id.nlm.nih.gov/mesh/");
t.linkedPredicate("a", "mesh:"+urlescape(code), null, null);
}
}
private void generateLinkedPredicate(Complex t, String code) throws FHIRException {
Expression expression = SnomedExpressions.parse(code);

View File

@ -122,10 +122,10 @@ public abstract class RdfParserBase extends ParserBase implements IParser {
return;
if ("http://snomed.info/sct".equals(element.getSystem())) {
t.prefix("sct", "http://snomed.info/sct/");
t.predicate("a", "sct:"+element.getCode());
t.predicate("a", "sct:"+element.getCode(), false);
} else if ("http://snomed.info/sct".equals(element.getSystem())) {
t.prefix("loinc", "http://loinc.org/rdf#");
t.predicate("a", "loinc:"+element.getCode());
t.predicate("a", "loinc:"+element.getCode(), false);
}
}

View File

@ -72,22 +72,35 @@ public class Turtle {
protected List<Predicate> predicates = new ArrayList<Predicate>();
public Complex predicate(String predicate, String object) {
return predicate(predicate, object, false);
}
public Complex predicate(String predicate, String object, boolean asList) {
predicateSet.add(predicate);
objectSet.add(object);
return predicate(predicate, new StringType(object));
return predicate(predicate, new StringType(object), asList);
}
public Complex linkedPredicate(String predicate, String object, String link, String comment) {
public Complex linkedPredicate(String predicate, String object, String link, String comment) {
return linkedPredicate(predicate, object, link, comment, false);
}
public Complex linkedPredicate(String predicate, String object, String link, String comment, boolean asList) {
predicateSet.add(predicate);
objectSet.add(object);
return linkedPredicate(predicate, new StringType(object), link, comment);
return linkedPredicate(predicate, new StringType(object), link, comment, asList);
}
public Complex predicate(String predicate, Triple object) {
return predicate(predicate, object, false);
}
public Complex predicate(String predicate, Triple object) {
public Complex predicate(String predicate, Triple object, boolean asList) {
Predicate p = getPredicate(predicate);
if (p == null) {
p = new Predicate();
p.predicate = predicate;
p.asList = asList;
predicateSet.add(predicate);
predicates.add(p);
}
@ -95,7 +108,7 @@ public class Turtle {
objectSet.add(((StringType) object).value);
p.objects.add(object);
return this;
}
}
protected Predicate getPredicate(String predicate) {
for (Predicate p : predicates)
@ -103,14 +116,19 @@ public class Turtle {
return p;
return null;
}
public Complex linkedPredicate(String predicate, Triple object, String link, String comment) {
return linkedPredicate(predicate, object, link, comment, false);
}
public Complex linkedPredicate(String predicate, Triple object, String link, String comment, boolean asList) {
Predicate p = getPredicate(predicate);
if (p == null) {
p = new Predicate();
p.predicate = predicate;
p.link = link;
p.comment = comment;
p.asList = asList;
predicateSet.add(predicate);
predicates.add(p);
}
@ -119,18 +137,26 @@ public class Turtle {
p.objects.add(object);
return this;
}
public Complex predicate(String predicate) {
return predicate(predicate, false);
}
public Complex predicate(String predicate) {
predicateSet.add(predicate);
Complex c = complex();
predicate(predicate, c);
return c;
}
public Complex predicate(String predicate, boolean asList) {
predicateSet.add(predicate);
Complex c = complex();
predicate(predicate, c, asList);
return c;
}
public Complex linkedPredicate(String predicate, String link, String comment) {
public Complex linkedPredicate(String predicate, String link, String comment) {
return linkedPredicate(predicate, link, comment, false);
}
public Complex linkedPredicate(String predicate, String link, String comment, boolean asList) {
predicateSet.add(predicate);
Complex c = complex();
linkedPredicate(predicate, c, link, comment);
linkedPredicate(predicate, c, link, comment, asList);
return c;
}
@ -144,6 +170,7 @@ public class Turtle {
protected String link;
protected List<Triple> objects = new ArrayList<Turtle.Triple>();
protected String comment;
protected boolean asList = false;
public String getPredicate() {
return predicate;
@ -183,15 +210,15 @@ public class Turtle {
public void comment(String comment) {
if (!Utilities.noString(comment)) {
predicate("rdfs:comment", literal(comment));
predicate("dcterms:description", literal(comment));
predicate("rdfs:comment", literal(comment), false);
predicate("dcterms:description", literal(comment), false);
}
}
public void label(String label) {
if (!Utilities.noString(label)) {
predicate("rdfs:label", literal(label));
predicate("dc:title", literal(label));
predicate("rdfs:label", literal(label), false);
predicate("dc:title", literal(label), false);
}
}
@ -445,7 +472,7 @@ public class Turtle {
writer.ln();
if (!section.comments.isEmpty()) {
for (String s : section.comments) {
writer.ln("# "+s);
writer.ln("# "+formatMultilineComment(s));
}
writer.ln();
}
@ -464,10 +491,13 @@ public class Turtle {
writer.write(" ");
boolean first = true;
for (Triple o : p.getObjects()) {
if (first)
if (first) {
first = false;
else
writer.write(", ");
if (p.asList) writer.write("( ");
} else {
if (!p.asList) writer.write(", ");
else writer.write(" ");
}
if (o instanceof StringType)
writer.write(((StringType) o).value);
else {
@ -478,10 +508,11 @@ public class Turtle {
writer.write("]");
}
}
String comment = p.comment == null? "" : " # "+p.comment;
String comment = p.comment == null? "" : " # "+formatMultilineComment(p.comment);
if (p.asList) writer.write(" )");
i++;
if (i < sbj.predicates.size())
writer.write(";"+comment+"\r\n ");
writer.write(" ;"+comment+"\r\n ");
else {
if (Utilities.noString(sbj.id))
writer.write("]");
@ -490,6 +521,11 @@ public class Turtle {
}
}
}
private String formatMultilineComment(String s) {
return s.replace("\n", "\n#");
}
private void commitSection(StringBuilder b, Section section) throws Exception {
b.append("# - "+section.name+" "+Utilities.padLeft("", '-', 75-section.name.length())+"\r\n");
@ -511,10 +547,12 @@ public class Turtle {
b.append(" ");
boolean first = true;
for (Triple o : p.getObjects()) {
if (first)
first = false;
else
b.append(", ");
if (first) {
first = false;
if (p.asList) b.append("( ");
} else
if (!p.asList) b.append(", ");
else b.append(" ");
if (o instanceof StringType)
b.append(Utilities.escapeXml(((StringType) o).value));
else {
@ -525,12 +563,13 @@ public class Turtle {
b.append("]");
}
}
String comment = p.comment == null? "" : " # "+p.comment;
String comment = p.comment == null? "" : " # "+formatMultilineComment(p.comment);
if (p.asList) b.append(" )");
i++;
if (i < sbj.predicates.size())
b.append(";"+Utilities.escapeXml(comment)+"\r\n ");
b.append(" ;"+Utilities.escapeXml(comment)+"\r\n ");
else
b.append("."+Utilities.escapeXml(comment)+"\r\n\r\n");
b.append(" ."+Utilities.escapeXml(comment)+"\r\n\r\n");
}
}
}
@ -566,8 +605,11 @@ public class Turtle {
if (first) {
first = false;
writer.write(left+" "+po.getPredicate()+" ");
} else
writer.write(", ");
if (po.asList) writer.write("( ");
} else {
if (!po.asList) writer.write(", ");
else writer.write(" ");
}
if (o instanceof StringType)
writer.write(((StringType) o).value);
else {
@ -578,9 +620,10 @@ public class Turtle {
writer.write(" ]");
}
}
if (po.asList) writer.write(" )");
i++;
if (i < complex.predicates.size())
writer.write(";");
writer.write(" ;");
if (!Utilities.noString(po.comment))
writer.write(" # "+escape(po.comment, false));
}
@ -602,9 +645,11 @@ public class Turtle {
for (Triple o : po.getObjects()) {
if (first) {
first = false;
b.append(left+" "+po.makelink()+" ");
if (po.asList) b.append(left+"( ");
b.append(po.makelink()+" ");
} else
b.append(", ");
if (!po.asList) b.append(", ");
else b.append(" ");
if (o instanceof StringType)
b.append(Utilities.escapeXml(((StringType) o).value));
else {
@ -615,9 +660,10 @@ public class Turtle {
b.append(" ]");
}
}
if (po.asList) b.append(" )");
i++;
if (i < complex.predicates.size())
b.append(";");
b.append(" ;");
if (!Utilities.noString(po.comment))
b.append(" # "+Utilities.escapeXml(escape(po.comment, false)));
}