
001package org.hl7.fhir.r5.elementmodel; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032 033 034import java.io.IOException; 035import java.io.InputStream; 036import java.io.OutputStream; 037import java.util.ArrayList; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Set; 041 042import org.hl7.fhir.exceptions.FHIRException; 043import org.hl7.fhir.exceptions.FHIRFormatError; 044import org.hl7.fhir.r5.context.IWorkerContext; 045import org.hl7.fhir.r5.elementmodel.Element.SpecialElement; 046import org.hl7.fhir.r5.formats.IParser.OutputStyle; 047import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 048import org.hl7.fhir.r5.model.StructureDefinition; 049import org.hl7.fhir.r5.utils.SnomedExpressions; 050import org.hl7.fhir.r5.utils.SnomedExpressions.Expression; 051import org.hl7.fhir.utilities.TextFile; 052import org.hl7.fhir.utilities.Utilities; 053import org.hl7.fhir.utilities.i18n.I18nConstants; 054import org.hl7.fhir.utilities.turtle.Turtle; 055import org.hl7.fhir.utilities.turtle.Turtle.Complex; 056import org.hl7.fhir.utilities.turtle.Turtle.Section; 057import org.hl7.fhir.utilities.turtle.Turtle.Subject; 058import org.hl7.fhir.utilities.turtle.Turtle.TTLComplex; 059import org.hl7.fhir.utilities.turtle.Turtle.TTLList; 060import org.hl7.fhir.utilities.turtle.Turtle.TTLLiteral; 061import org.hl7.fhir.utilities.turtle.Turtle.TTLObject; 062import org.hl7.fhir.utilities.turtle.Turtle.TTLURL; 063import org.hl7.fhir.utilities.validation.ValidationMessage; 064import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 065import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 066 067 068public class TurtleParser extends ParserBase { 069 070 private String base; 071 072 private OutputStyle style; 073 074 public static String FHIR_URI_BASE = "http://hl7.org/fhir/"; 075 public static String FHIR_VERSION_BASE = "http://build.fhir.org/"; 076 077 public TurtleParser(IWorkerContext context) { 078 super(context); 079 } 080 @Override 081 public List<NamedElement> parse(InputStream input) throws IOException, FHIRException { 082 List<NamedElement> res = new ArrayList<>(); 083 Turtle src = new Turtle(); 084 if (policy == ValidationPolicy.EVERYTHING) { 085 try { 086 src.parse(TextFile.streamToString(input)); 087 } catch (Exception e) { 088 logError(ValidationMessage.NO_RULE_DATE, -1, -1, "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_TURTLE_, e.getMessage()), IssueSeverity.FATAL); 089 return null; 090 } 091 Element e = parse(src); 092 if (e != null) { 093 res.add(new NamedElement(null, e)); 094 } 095 } else { 096 src.parse(TextFile.streamToString(input)); 097 Element e = parse(src); 098 if (e != null) { 099 res.add(new NamedElement(null, e)); 100 } 101 } 102 return res; 103 } 104 105 private Element parse(Turtle src) throws FHIRException { 106 // we actually ignore the stated URL here 107 for (TTLComplex cmp : src.getObjects().values()) { 108 for (String p : cmp.getPredicates().keySet()) { 109 if ((FHIR_URI_BASE + "nodeRole").equals(p) && cmp.getPredicates().get(p).hasValue(FHIR_URI_BASE + "treeRoot")) { 110 return parse(src, cmp); 111 } 112 } 113 } 114 // still here: well, we didn't find a start point 115 String msg = "Error parsing Turtle: unable to find any node maked as the entry point (where " + FHIR_URI_BASE + "nodeRole = " + FHIR_URI_BASE + "treeRoot)"; 116 if (policy == ValidationPolicy.EVERYTHING) { 117 logError(ValidationMessage.NO_RULE_DATE, -1, -1, "(document)", IssueType.INVALID, msg, IssueSeverity.FATAL); 118 return null; 119 } else { 120 throw new FHIRFormatError(msg); 121 } 122 } 123 124 private Element parse(Turtle src, TTLComplex cmp) throws FHIRException { 125 TTLObject type = cmp.getPredicates().get("http://www.w3.org/2000/01/rdf-schema#type"); 126 if (type == null) { 127 logError(ValidationMessage.NO_RULE_DATE, cmp.getLine(), cmp.getCol(), "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.UNKNOWN_RESOURCE_TYPE_MISSING_RDFSTYPE), IssueSeverity.FATAL); 128 return null; 129 } 130 if (type instanceof TTLList) { 131 // this is actually broken - really we have to look through the structure definitions at this point 132 for (TTLObject obj : ((TTLList) type).getList()) { 133 if (obj instanceof TTLURL && ((TTLURL) obj).getUri().startsWith(FHIR_URI_BASE)) { 134 type = obj; 135 break; 136 } 137 } 138 } 139 if (!(type instanceof TTLURL)) { 140 logError(ValidationMessage.NO_RULE_DATE, cmp.getLine(), cmp.getCol(), "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.UNEXPECTED_DATATYPE_FOR_RDFSTYPE), IssueSeverity.FATAL); 141 return null; 142 } 143 String name = ((TTLURL) type).getUri(); 144 String ns = name.substring(0, name.lastIndexOf("/")); 145 name = name.substring(name.lastIndexOf("/")+1); 146 String path = "/"+name; 147 148 StructureDefinition sd = getDefinition(cmp.getLine(), cmp.getCol(), ns, name); 149 if (sd == null) 150 return null; 151 152 Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd)); 153 result.markLocation(cmp.getLine(), cmp.getCol()); 154 result.setType(name); 155 parseChildren(src, path, cmp, result, false); 156 result.numberChildren(); 157 return result; 158 } 159 160 private void parseChildren(Turtle src, String path, TTLComplex object, Element element, boolean primitive) throws FHIRException { 161 162 List<Property> properties = element.getProperty().getChildProperties(element.getName(), null); 163 Set<String> processed = new HashSet<String>(); 164 if (primitive) 165 processed.add(FHIR_URI_BASE + "value"); 166 167 // note that we do not trouble ourselves to maintain the wire format order here - we don't even know what it was anyway 168 // first pass: process the properties 169 for (Property property : properties) { 170 if (property.isChoice()) { 171 for (TypeRefComponent type : property.getDefinition().getType()) { 172 String eName = property.getName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getCode()); 173 parseChild(src, object, element, processed, property, path, getFormalName(property, eName)); 174 } 175 } else { 176 parseChild(src, object, element, processed, property, path, getFormalName(property)); 177 } 178 } 179 180 // second pass: check for things not processed 181 if (policy != ValidationPolicy.NONE) { 182 for (String u : object.getPredicates().keySet()) { 183 if (!processed.contains(u)) { 184 TTLObject n = object.getPredicates().get(u); 185 logError(ValidationMessage.NO_RULE_DATE, n.getLine(), n.getCol(), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PREDICATE_, u), IssueSeverity.ERROR); 186 } 187 } 188 } 189 } 190 191 private void parseChild(Turtle src, TTLComplex object, Element context, Set<String> processed, Property property, String path, String name) throws FHIRException { 192 processed.add(name); 193 String npath = path+"/"+property.getName(); 194 TTLObject e = object.getPredicates().get(FHIR_URI_BASE + name); 195 if (e == null) 196 return; 197 if (property.isList() && (e instanceof TTLList)) { 198 TTLList arr = (TTLList) e; 199 for (TTLObject am : arr.getList()) { 200 parseChildInstance(src, npath, object, context, property, name, am); 201 } 202 } else { 203 parseChildInstance(src, npath, object, context, property, name, e); 204 } 205 } 206 207 private void parseChildInstance(Turtle src, String npath, TTLComplex object, Element element, Property property, String name, TTLObject e) throws FHIRException { 208 if (property.isResource()) 209 parseResource(src, npath, object, element, property, name, e); 210 else if (e instanceof TTLComplex) { 211 TTLComplex child = (TTLComplex) e; 212 Element n = new Element(tail(name), property).markLocation(e.getLine(), e.getCol()); 213 element.getChildren().add(n); 214 if (property.isPrimitive(property.getType(tail(name)))) { 215 parseChildren(src, npath, child, n, true); 216 TTLObject val = child.getPredicates().get(FHIR_URI_BASE + "value"); 217 if (val != null) { 218 if (val instanceof TTLLiteral) { 219 String value = ((TTLLiteral) val).getValue(); 220 String type = ((TTLLiteral) val).getType(); 221 // todo: check type 222 n.setValue(value); 223 } else 224 logError(ValidationMessage.NO_RULE_DATE, object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_A_LITERAL_NOT_, "a "+e.getClass().getName()), IssueSeverity.ERROR); 225 } 226 } else 227 parseChildren(src, npath, child, n, false); 228 229 } else 230 logError(ValidationMessage.NO_RULE_DATE, object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_A_URI_OR_BNODE_NOT_, "a "+e.getClass().getName()), IssueSeverity.ERROR); 231 } 232 233 234 private String tail(String name) { 235 return name.substring(name.lastIndexOf(".")+1); 236 } 237 238 private void parseResource(Turtle src, String npath, TTLComplex object, Element element, Property property, String name, TTLObject e) throws FHIRException { 239 TTLComplex obj; 240 if (e instanceof TTLComplex) 241 obj = (TTLComplex) e; 242 else if (e instanceof TTLURL) { 243 String url = ((TTLURL) e).getUri(); 244 obj = src.getObject(url); 245 if (obj == null) { 246 logError(ValidationMessage.NO_RULE_DATE, e.getLine(), e.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.REFERENCE_TO__CANNOT_BE_RESOLVED, url), IssueSeverity.FATAL); 247 return; 248 } 249 } else 250 throw new FHIRFormatError(context.formatMessage(I18nConstants.WRONG_TYPE_FOR_RESOURCE)); 251 252 TTLObject type = obj.getPredicates().get("http://www.w3.org/2000/01/rdf-schema#type"); 253 if (type == null) { 254 logError(ValidationMessage.NO_RULE_DATE, object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNKNOWN_RESOURCE_TYPE_MISSING_RDFSTYPE), IssueSeverity.FATAL); 255 return; 256 } 257 if (type instanceof TTLList) { 258 // this is actually broken - really we have to look through the structure definitions at this point 259 for (TTLObject tobj : ((TTLList) type).getList()) { 260 if (tobj instanceof TTLURL && ((TTLURL) tobj).getUri().startsWith(FHIR_URI_BASE)) { 261 type = tobj; 262 break; 263 } 264 } 265 } 266 if (!(type instanceof TTLURL)) { 267 logError(ValidationMessage.NO_RULE_DATE, object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNEXPECTED_DATATYPE_FOR_RDFSTYPE), IssueSeverity.FATAL); 268 return; 269 } 270 String rt = ((TTLURL) type).getUri(); 271 String ns = rt.substring(0, rt.lastIndexOf("/")); 272 rt = rt.substring(rt.lastIndexOf("/")+1); 273 274 StructureDefinition sd = getDefinition(object.getLine(), object.getCol(), ns, rt); 275 if (sd == null) 276 return; 277 278 Element n = new Element(tail(name), property).markLocation(object.getLine(), object.getCol()); 279 element.getChildren().add(n); 280 n.updateProperty(new Property(this.context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(n.getProperty()), property); 281 n.setType(rt); 282 parseChildren(src, npath, obj, n, false); 283 } 284 285 private String getFormalName(Property property) { 286 String en = property.getDefinition().getBase().getPath(); 287 if (en == null) 288 en = property.getDefinition().getPath(); 289// boolean doType = false; 290// if (en.endsWith("[x]")) { 291// en = en.substring(0, en.length()-3); 292// doType = true; 293// } 294// if (doType || (element.getProperty().getDefinition().getType().size() > 1 && !allReference(element.getProperty().getDefinition().getType()))) 295// en = en + Utilities.capitalize(element.getType()); 296 return en; 297 } 298 299 private String getFormalName(Property property, String elementName) { 300 String en = property.getDefinition().getBase().getPath(); 301 if (en == null) 302 en = property.getDefinition().getPath(); 303 if (!en.endsWith("[x]")) 304 throw new Error(context.formatMessage(I18nConstants.ATTEMPT_TO_REPLACE_ELEMENT_NAME_FOR_A_NONCHOICE_TYPE)); 305 return en.substring(0, en.lastIndexOf(".")+1)+elementName; 306 } 307 308 @Override 309 public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException, FHIRException { 310 this.base = base; 311 this.style = style; 312 313 Turtle ttl = new Turtle(); 314 compose(e, ttl, base); 315 ttl.commit(stream, false); 316 } 317 318 public void compose(Element e, Turtle ttl, String base) throws FHIRException { 319 if (e.getPath() == null) { 320 e.populatePaths(null); 321 } 322 323 ttl.prefix("fhir", FHIR_URI_BASE); 324 ttl.prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); 325 ttl.prefix("owl", "http://www.w3.org/2002/07/owl#"); 326 ttl.prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); 327 328 Section section = ttl.section("resource"); 329 if (style == OutputStyle.PRETTY) { 330 for (String s : e.getComments()) { 331 section.stringComment(s); 332 } 333 } 334 String subjId = genSubjectId(e); 335 336 Subject subject; 337 if (hasModifierExtension(e)) 338 subject = section.triple(subjId, "a", "fhir:_" + e.getType()); 339 else 340 subject = section.triple(subjId, "a", "fhir:" + e.getType()); 341 342 subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root"), null); 343 344 for (Element child : e.getChildren()) { 345 composeElement(section, subject, child, null); 346 } 347 348 } 349 350 private boolean hasModifierExtension(Element e) { 351 return e.getChildren().stream().anyMatch(p -> p.getName().equals("modifierExtension")); 352 } 353 354 protected String getURIType(String uri) { 355 if(uri.startsWith("<" + FHIR_URI_BASE)) 356 if(uri.substring(FHIR_URI_BASE.length() + 1).contains("/")) 357 return uri.substring(FHIR_URI_BASE.length() + 1, uri.indexOf('/', FHIR_URI_BASE.length() + 1)); 358 return null; 359 } 360 361 protected String getReferenceURI(String ref) { 362 if (ref != null && (ref.startsWith("http://") || ref.startsWith("https://"))) 363 return "<" + ref + ">"; 364 else if (base != null && ref != null && ref.contains("/")) 365 return "<" + Utilities.appendForwardSlash(base) + ref + ">"; 366 else 367 return null; 368 } 369 370 protected void decorateReference(Complex t, Element coding) { 371 String refURI = getReferenceURI(coding.getChildValue("reference")); 372 if(refURI != null) 373 t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference"), null); 374 } 375 376 protected void decorateCanonical(Complex t, Element canonical) { 377 String refURI = getReferenceURI(canonical.primitiveValue()); 378 if(refURI != null) 379 t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference"), null); 380 } 381 382 private String genSubjectId(Element e) { 383 String id = e.getChildValue("id"); 384 if (base == null || id == null) 385 return ""; 386 else if (base.endsWith("#")) 387 return "<" + base + e.getType() + "-" + id + ">"; 388 else 389 return "<" + Utilities.pathURL(base, e.getType(), id) + ">"; 390 } 391 392 private String urlescape(String s) { 393 StringBuilder b = new StringBuilder(); 394 for (char ch : s.toCharArray()) { 395 if (Utilities.charInSet(ch, ':', ';', '=', ',')) 396 b.append("%"+Integer.toHexString(ch)); 397 else 398 b.append(ch); 399 } 400 return b.toString(); 401 } 402 403 private void composeElement(Section section, Complex ctxt, Element element, Element parent) throws FHIRException { 404// "Extension".equals(element.getType())? 405// (element.getProperty().getDefinition().getIsModifier()? "modifierExtension" : "extension") ; 406 407 String en = getFormalName(element); 408 409 if (!wantCompose(parent == null ? "" : parent.getPath(), element)) { 410 return; 411 } 412 413 String comment = null; 414 if (style == OutputStyle.PRETTY) { 415 comment = String.join(", ", element.getComments()); 416 } 417 Complex t; 418 if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY && parent != null && parent.getNamedChildValue("fullUrl") != null) { 419 String url = "<"+parent.getNamedChildValue("fullUrl")+">"; 420 ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment, element.getProperty().isList()); 421 t = section.subject(url); 422 } else { 423 t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty()), comment, element.getProperty().isList()); 424 } 425 if (element.getProperty().getName().endsWith("[x]") && !element.hasValue()) { 426 t.linkedPredicate("a", "fhir:" + element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType()), null); 427 } 428 if (element.getSpecial() != null) 429 t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType()), null); 430 if (element.hasValue()) 431 t.linkedPredicate("fhir:v", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType()), null); 432 433 if ("Coding".equals(element.getType())) 434 decorateCoding(t, element, section); 435 if (Utilities.existsInList(element.getType(), "Reference")) 436 decorateReference(t, element); 437 else if (Utilities.existsInList(element.getType(), "canonical")) 438 decorateCanonical(t, element); 439 440 if("canonical".equals(element.getType())) { 441 String refURI = element.primitiveValue(); 442 if (refURI != null) { 443 String uriType = getURIType(refURI); 444 if(uriType != null && !section.hasSubject(refURI)) 445 section.triple(refURI, "a", "fhir:" + uriType); 446 } 447 } 448 449 if("Reference".equals(element.getType())) { 450 String refURI = getReferenceURI(element.getChildValue("reference")); 451 if (refURI != null) { 452 String uriType = getURIType(refURI); 453 if(uriType != null && !section.hasSubject(refURI)) 454 section.triple(refURI, "a", "fhir:" + uriType); 455 } 456 } 457 458 for (Element child : element.getChildren()) { 459 if ("xhtml".equals(child.getType())) { 460 String childfn = getFormalName(child); 461 t.predicate("fhir:" + childfn, ttlLiteral(child.getValue(), child.getType())); 462 } else 463 composeElement(section, t, child, element); 464 } 465 } 466 467 private String getFormalName(Element element) { 468 String en = null; 469 if (element.getSpecial() == null) 470 en = element.getProperty().getName(); 471 else if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY) 472 en = "resource"; 473 else if (element.getSpecial() == SpecialElement.BUNDLE_OUTCOME) 474 en = "outcome"; 475 else if (element.getSpecial() == SpecialElement.BUNDLE_ISSUES) 476 en = "issues"; 477 else if (element.getSpecial() == SpecialElement.PARAMETER) 478 en = element.getElementProperty().getDefinition().getPath(); 479 else // CONTAINED 480 en = "contained"; 481 482 if (en == null) 483 en = element.getProperty().getName(); 484 485 if (en.endsWith("[x]")) 486 en = en.substring(0, en.length()-3); 487 488 if (hasModifierExtension(element)) 489 return "_" + en; 490 else 491 return en; 492 } 493 494 static public String ttlLiteral(String value, String type) { 495 String xst = ""; 496 if (type.equals("boolean")) 497 xst = "^^xsd:boolean"; 498 else if (type.equals("integer")) 499 xst = "^^xsd:integer"; 500 else if (type.equals("integer64")) 501 xst = "^^xsd:long"; 502 else if (type.equals("unsignedInt")) 503 xst = "^^xsd:nonNegativeInteger"; 504 else if (type.equals("positiveInt")) 505 xst = "^^xsd:positiveInteger"; 506 else if (type.equals("decimal")) 507 xst = "^^xsd:decimal"; 508 else if (type.equals("base64Binary")) 509 xst = "^^xsd:base64Binary"; 510 else if (type.equals("canonical") || type.equals("oid") || type.equals("uri") || type.equals("url") || type.equals("uuid")) 511 xst = "^^xsd:anyURI"; 512 else if (type.equals("instant")) 513 xst = "^^xsd:dateTime"; 514 else if (type.equals("time")) 515 xst = "^^xsd:time"; 516 else if (type.equals("date") || type.equals("dateTime") ) { 517 String v = value; 518 if (v.length() > 10) { 519 int i = value.substring(10).indexOf("-"); 520 if (i == -1) 521 i = value.substring(10).indexOf("+"); 522 v = i == -1 ? value : value.substring(0, 10+i); 523 } 524 if (v.length() > 10) 525 xst = "^^xsd:dateTime"; 526 else if (v.length() == 10) 527 xst = "^^xsd:date"; 528 else if (v.length() == 7) 529 xst = "^^xsd:gYearMonth"; 530 else if (v.length() == 4) 531 xst = "^^xsd:gYear"; 532 } 533 534 return "\"" +Turtle.escape(value, true) + "\""+xst; 535 } 536 537 protected void decorateCoding(Complex t, Element coding, Section section) throws FHIRException { 538 String system = coding.getChildValue("system"); 539 String code = coding.getChildValue("code"); 540 541 if (system == null || code == null) 542 return; 543 if ("http://snomed.info/sct".equals(system)) { 544 t.prefix("sct", "http://snomed.info/id/"); 545 if (code.contains(":") || code.contains("=")) 546 generateLinkedPredicate(t, code); 547 else 548 t.linkedPredicate("a", "sct:" + urlescape(code), null, null); 549 } else if ("http://loinc.org".equals(system)) { 550 t.prefix("loinc", "https://loinc.org/rdf/"); 551 t.linkedPredicate("a", "loinc:"+urlescape(code).toUpperCase(), null, null); 552 } else if ("https://www.nlm.nih.gov/mesh".equals(system)) { 553 t.prefix("mesh", "http://id.nlm.nih.gov/mesh/"); 554 t.linkedPredicate("a", "mesh:"+urlescape(code), null, null); 555 } 556 } 557 558 private void generateLinkedPredicate(Complex t, String code) throws FHIRException { 559 Expression expression = SnomedExpressions.parse(code); 560 561 } 562 public OutputStyle getStyle() { 563 return style; 564 } 565 public void setStyle(OutputStyle style) { 566 this.style = style; 567 } 568 569 570// 128045006|cellulitis (disorder)|:{363698007|finding site|=56459004|foot structure|} 571// Grahame Grieve: or 572// 573// 64572001|disease|:{116676008|associated morphology|=72704001|fracture|,363698007|finding site|=(12611008|bone structure of tibia|:272741003|laterality|=7771000|left|)} 574// Harold Solbrig: 575// a sct:128045006, 576// rdfs:subClassOf [ 577// a owl:Restriction; 578// owl:onProperty sct:609096000 ; 579// owl:someValuesFrom [ 580// a owl:Restriction; 581// owl:onProperty sct:363698007 ; 582// owl:someValuesFrom sct:56459004 ] ] ; 583// and 584// 585// a sct:64572001, 586// rdfs:subclassOf [ 587// a owl:Restriction ; 588// owl:onProperty sct:60909600 ; 589// owl:someValuesFrom [ 590// a owl:Class ; 591// owl:intersectionOf ( [ 592// a owl:Restriction; 593// owl:onProperty sct:116676008; 594// owl:someValuesFrom sct:72704001 ] 595// [ a owl:Restriction; 596// owl:onProperty sct:363698007 597// owl:someValuesFrom [ 598// a owl:Class ; 599// owl:intersectionOf( 600// sct:12611008 601// owl:someValuesFrom [ 602// a owl:Restriction; 603// owl:onProperty sct:272741003; 604// owl:someValuesFrom sct:7771000 605// ] ) ] ] ) ] ] 606// (an approximation -- I'll have to feed it into a translator to be sure I've got it 100% right) 607// 608 609}