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.elementmodel.ParserBase.NamedElement; 047import org.hl7.fhir.r5.formats.IParser.OutputStyle; 048import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 049import org.hl7.fhir.r5.model.StructureDefinition; 050import org.hl7.fhir.r5.utils.SnomedExpressions; 051import org.hl7.fhir.r5.utils.SnomedExpressions.Expression; 052import org.hl7.fhir.utilities.TextFile; 053import org.hl7.fhir.utilities.Utilities; 054import org.hl7.fhir.utilities.i18n.I18nConstants; 055import org.hl7.fhir.utilities.turtle.Turtle; 056import org.hl7.fhir.utilities.turtle.Turtle.Complex; 057import org.hl7.fhir.utilities.turtle.Turtle.Section; 058import org.hl7.fhir.utilities.turtle.Turtle.Subject; 059import org.hl7.fhir.utilities.turtle.Turtle.TTLComplex; 060import org.hl7.fhir.utilities.turtle.Turtle.TTLList; 061import org.hl7.fhir.utilities.turtle.Turtle.TTLLiteral; 062import org.hl7.fhir.utilities.turtle.Turtle.TTLObject; 063import org.hl7.fhir.utilities.turtle.Turtle.TTLURL; 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 public static String FHIR_URI_BASE = "http://hl7.org/fhir/"; 073 public static String FHIR_VERSION_BASE = "http://build.fhir.org/"; 074 075 public TurtleParser(IWorkerContext context) { 076 super(context); 077 } 078 @Override 079 public List<NamedElement> parse(InputStream input) throws IOException, FHIRException { 080 List<NamedElement> res = new ArrayList<>(); 081 Turtle src = new Turtle(); 082 if (policy == ValidationPolicy.EVERYTHING) { 083 try { 084 src.parse(TextFile.streamToString(input)); 085 } catch (Exception e) { 086 logError(-1, -1, "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_TURTLE_, e.getMessage()), IssueSeverity.FATAL); 087 return null; 088 } 089 Element e = parse(src); 090 if (e != null) { 091 res.add(new NamedElement(null, e)); 092 } 093 } else { 094 src.parse(TextFile.streamToString(input)); 095 Element e = parse(src); 096 if (e != null) { 097 res.add(new NamedElement(null, e)); 098 } 099 } 100 return res; 101 } 102 103 private Element parse(Turtle src) throws FHIRException { 104 // we actually ignore the stated URL here 105 for (TTLComplex cmp : src.getObjects().values()) { 106 for (String p : cmp.getPredicates().keySet()) { 107 if ((FHIR_URI_BASE + "nodeRole").equals(p) && cmp.getPredicates().get(p).hasValue(FHIR_URI_BASE + "treeRoot")) { 108 return parse(src, cmp); 109 } 110 } 111 } 112 // still here: well, we didn't find a start point 113 String msg = "Error parsing Turtle: unable to find any node maked as the entry point (where " + FHIR_URI_BASE + "nodeRole = " + FHIR_URI_BASE + "treeRoot)"; 114 if (policy == ValidationPolicy.EVERYTHING) { 115 logError(-1, -1, "(document)", IssueType.INVALID, msg, IssueSeverity.FATAL); 116 return null; 117 } else { 118 throw new FHIRFormatError(msg); 119 } 120 } 121 122 private Element parse(Turtle src, TTLComplex cmp) throws FHIRException { 123 TTLObject type = cmp.getPredicates().get("http://www.w3.org/2000/01/rdf-schema#type"); 124 if (type == null) { 125 logError(cmp.getLine(), cmp.getCol(), "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.UNKNOWN_RESOURCE_TYPE_MISSING_RDFSTYPE), IssueSeverity.FATAL); 126 return null; 127 } 128 if (type instanceof TTLList) { 129 // this is actually broken - really we have to look through the structure definitions at this point 130 for (TTLObject obj : ((TTLList) type).getList()) { 131 if (obj instanceof TTLURL && ((TTLURL) obj).getUri().startsWith(FHIR_URI_BASE)) { 132 type = obj; 133 break; 134 } 135 } 136 } 137 if (!(type instanceof TTLURL)) { 138 logError(cmp.getLine(), cmp.getCol(), "(document)", IssueType.INVALID, context.formatMessage(I18nConstants.UNEXPECTED_DATATYPE_FOR_RDFSTYPE), IssueSeverity.FATAL); 139 return null; 140 } 141 String name = ((TTLURL) type).getUri(); 142 String ns = name.substring(0, name.lastIndexOf("/")); 143 name = name.substring(name.lastIndexOf("/")+1); 144 String path = "/"+name; 145 146 StructureDefinition sd = getDefinition(cmp.getLine(), cmp.getCol(), ns, name); 147 if (sd == null) 148 return null; 149 150 Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd)); 151 result.markLocation(cmp.getLine(), cmp.getCol()); 152 result.setType(name); 153 parseChildren(src, path, cmp, result, false); 154 result.numberChildren(); 155 return result; 156 } 157 158 private void parseChildren(Turtle src, String path, TTLComplex object, Element element, boolean primitive) throws FHIRException { 159 160 List<Property> properties = element.getProperty().getChildProperties(element.getName(), null); 161 Set<String> processed = new HashSet<String>(); 162 if (primitive) 163 processed.add(FHIR_URI_BASE + "value"); 164 165 // note that we do not trouble ourselves to maintain the wire format order here - we don't even know what it was anyway 166 // first pass: process the properties 167 for (Property property : properties) { 168 if (property.isChoice()) { 169 for (TypeRefComponent type : property.getDefinition().getType()) { 170 String eName = property.getName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getCode()); 171 parseChild(src, object, element, processed, property, path, getFormalName(property, eName)); 172 } 173 } else { 174 parseChild(src, object, element, processed, property, path, getFormalName(property)); 175 } 176 } 177 178 // second pass: check for things not processed 179 if (policy != ValidationPolicy.NONE) { 180 for (String u : object.getPredicates().keySet()) { 181 if (!processed.contains(u)) { 182 TTLObject n = object.getPredicates().get(u); 183 logError(n.getLine(), n.getCol(), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PREDICATE_, u), IssueSeverity.ERROR); 184 } 185 } 186 } 187 } 188 189 private void parseChild(Turtle src, TTLComplex object, Element context, Set<String> processed, Property property, String path, String name) throws FHIRException { 190 processed.add(name); 191 String npath = path+"/"+property.getName(); 192 TTLObject e = object.getPredicates().get(FHIR_URI_BASE + name); 193 if (e == null) 194 return; 195 if (property.isList() && (e instanceof TTLList)) { 196 TTLList arr = (TTLList) e; 197 for (TTLObject am : arr.getList()) { 198 parseChildInstance(src, npath, object, context, property, name, am); 199 } 200 } else { 201 parseChildInstance(src, npath, object, context, property, name, e); 202 } 203 } 204 205 private void parseChildInstance(Turtle src, String npath, TTLComplex object, Element element, Property property, String name, TTLObject e) throws FHIRException { 206 if (property.isResource()) 207 parseResource(src, npath, object, element, property, name, e); 208 else if (e instanceof TTLComplex) { 209 TTLComplex child = (TTLComplex) e; 210 Element n = new Element(tail(name), property).markLocation(e.getLine(), e.getCol()); 211 element.getChildren().add(n); 212 if (property.isPrimitive(property.getType(tail(name)))) { 213 parseChildren(src, npath, child, n, true); 214 TTLObject val = child.getPredicates().get(FHIR_URI_BASE + "value"); 215 if (val != null) { 216 if (val instanceof TTLLiteral) { 217 String value = ((TTLLiteral) val).getValue(); 218 String type = ((TTLLiteral) val).getType(); 219 // todo: check type 220 n.setValue(value); 221 } else 222 logError(object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_A_LITERAL_NOT_, "a "+e.getClass().getName()), IssueSeverity.ERROR); 223 } 224 } else 225 parseChildren(src, npath, child, n, false); 226 227 } else 228 logError(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); 229 } 230 231 232 private String tail(String name) { 233 return name.substring(name.lastIndexOf(".")+1); 234 } 235 236 private void parseResource(Turtle src, String npath, TTLComplex object, Element element, Property property, String name, TTLObject e) throws FHIRException { 237 TTLComplex obj; 238 if (e instanceof TTLComplex) 239 obj = (TTLComplex) e; 240 else if (e instanceof TTLURL) { 241 String url = ((TTLURL) e).getUri(); 242 obj = src.getObject(url); 243 if (obj == null) { 244 logError(e.getLine(), e.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.REFERENCE_TO__CANNOT_BE_RESOLVED, url), IssueSeverity.FATAL); 245 return; 246 } 247 } else 248 throw new FHIRFormatError(context.formatMessage(I18nConstants.WRONG_TYPE_FOR_RESOURCE)); 249 250 TTLObject type = obj.getPredicates().get("http://www.w3.org/2000/01/rdf-schema#type"); 251 if (type == null) { 252 logError(object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNKNOWN_RESOURCE_TYPE_MISSING_RDFSTYPE), IssueSeverity.FATAL); 253 return; 254 } 255 if (type instanceof TTLList) { 256 // this is actually broken - really we have to look through the structure definitions at this point 257 for (TTLObject tobj : ((TTLList) type).getList()) { 258 if (tobj instanceof TTLURL && ((TTLURL) tobj).getUri().startsWith(FHIR_URI_BASE)) { 259 type = tobj; 260 break; 261 } 262 } 263 } 264 if (!(type instanceof TTLURL)) { 265 logError(object.getLine(), object.getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNEXPECTED_DATATYPE_FOR_RDFSTYPE), IssueSeverity.FATAL); 266 return; 267 } 268 String rt = ((TTLURL) type).getUri(); 269 String ns = rt.substring(0, rt.lastIndexOf("/")); 270 rt = rt.substring(rt.lastIndexOf("/")+1); 271 272 StructureDefinition sd = getDefinition(object.getLine(), object.getCol(), ns, rt); 273 if (sd == null) 274 return; 275 276 Element n = new Element(tail(name), property).markLocation(object.getLine(), object.getCol()); 277 element.getChildren().add(n); 278 n.updateProperty(new Property(this.context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(n.getProperty()), property); 279 n.setType(rt); 280 parseChildren(src, npath, obj, n, false); 281 } 282 283 private String getFormalName(Property property) { 284 String en = property.getDefinition().getBase().getPath(); 285 if (en == null) 286 en = property.getDefinition().getPath(); 287// boolean doType = false; 288// if (en.endsWith("[x]")) { 289// en = en.substring(0, en.length()-3); 290// doType = true; 291// } 292// if (doType || (element.getProperty().getDefinition().getType().size() > 1 && !allReference(element.getProperty().getDefinition().getType()))) 293// en = en + Utilities.capitalize(element.getType()); 294 return en; 295 } 296 297 private String getFormalName(Property property, String elementName) { 298 String en = property.getDefinition().getBase().getPath(); 299 if (en == null) 300 en = property.getDefinition().getPath(); 301 if (!en.endsWith("[x]")) 302 throw new Error(context.formatMessage(I18nConstants.ATTEMPT_TO_REPLACE_ELEMENT_NAME_FOR_A_NONCHOICE_TYPE)); 303 return en.substring(0, en.lastIndexOf(".")+1)+elementName; 304 } 305 306 307 @Override 308 public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException, FHIRException { 309 this.base = base; 310 311 Turtle ttl = new Turtle(); 312 compose(e, ttl, base); 313 ttl.commit(stream, false); 314 } 315 316 317 318 public void compose(Element e, Turtle ttl, String base) throws FHIRException { 319 ttl.prefix("fhir", FHIR_URI_BASE); 320 ttl.prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); 321 ttl.prefix("owl", "http://www.w3.org/2002/07/owl#"); 322 ttl.prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); 323 324 325 Section section = ttl.section("resource"); 326 String subjId = genSubjectId(e); 327 328 String ontologyId = subjId.replace(">", ".ttl>"); 329 Section ontology = ttl.section("ontology header"); 330 ontology.triple(ontologyId, "a", "owl:Ontology"); 331 ontology.triple(ontologyId, "owl:imports", "fhir:fhir.ttl"); 332 if(ontologyId.startsWith("<" + FHIR_URI_BASE)) 333 ontology.triple(ontologyId, "owl:versionIRI", ontologyId.replace(FHIR_URI_BASE, FHIR_VERSION_BASE)); 334 335 Subject subject = section.triple(subjId, "a", "fhir:" + e.getType()); 336 subject.linkedPredicate("fhir:nodeRole", "fhir:treeRoot", linkResolver == null ? null : linkResolver.resolvePage("rdf.html#tree-root")); 337 338 for (Element child : e.getChildren()) { 339 composeElement(section, subject, child, null); 340 } 341 342 } 343 344 protected String getURIType(String uri) { 345 if(uri.startsWith("<" + FHIR_URI_BASE)) 346 if(uri.substring(FHIR_URI_BASE.length() + 1).contains("/")) 347 return uri.substring(FHIR_URI_BASE.length() + 1, uri.indexOf('/', FHIR_URI_BASE.length() + 1)); 348 return null; 349 } 350 351 protected String getReferenceURI(String ref) { 352 if (ref != null && (ref.startsWith("http://") || ref.startsWith("https://"))) 353 return "<" + ref + ">"; 354 else if (base != null && ref != null && ref.contains("/")) 355 return "<" + Utilities.appendForwardSlash(base) + ref + ">"; 356 else 357 return null; 358 } 359 360 protected void decorateReference(Complex t, Element coding) { 361 String refURI = getReferenceURI(coding.getChildValue("reference")); 362 if(refURI != null) 363 t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference")); 364 } 365 366 protected void decorateCanonical(Complex t, Element canonical) { 367 String refURI = getReferenceURI(canonical.primitiveValue()); 368 if(refURI != null) 369 t.linkedPredicate("fhir:link", refURI, linkResolver == null ? null : linkResolver.resolvePage("rdf.html#reference")); 370 } 371 372 private String genSubjectId(Element e) { 373 String id = e.getChildValue("id"); 374 if (base == null || id == null) 375 return ""; 376 else if (base.endsWith("#")) 377 return "<" + base + e.getType() + "-" + id + ">"; 378 else 379 return "<" + Utilities.pathURL(base, e.getType(), id) + ">"; 380 } 381 382 private String urlescape(String s) { 383 StringBuilder b = new StringBuilder(); 384 for (char ch : s.toCharArray()) { 385 if (Utilities.charInSet(ch, ':', ';', '=', ',')) 386 b.append("%"+Integer.toHexString(ch)); 387 else 388 b.append(ch); 389 } 390 return b.toString(); 391 } 392 393 private void composeElement(Section section, Complex ctxt, Element element, Element parent) throws FHIRException { 394// "Extension".equals(element.getType())? 395// (element.getProperty().getDefinition().getIsModifier()? "modifierExtension" : "extension") ; 396 String en = getFormalName(element); 397 398 Complex t; 399 if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY && parent != null && parent.getNamedChildValue("fullUrl") != null) { 400 String url = "<"+parent.getNamedChildValue("fullUrl")+">"; 401 ctxt.linkedPredicate("fhir:"+en, url, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); 402 t = section.subject(url); 403 } else { 404 t = ctxt.linkedPredicate("fhir:"+en, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); 405 } 406 if (element.getSpecial() != null) 407 t.linkedPredicate("a", "fhir:"+element.fhirType(), linkResolver == null ? null : linkResolver.resolveType(element.fhirType())); 408 if (element.hasValue()) 409 t.linkedPredicate("fhir:value", ttlLiteral(element.getValue(), element.getType()), linkResolver == null ? null : linkResolver.resolveType(element.getType())); 410 if (element.getProperty().isList() && (!element.isResource() || element.getSpecial() == SpecialElement.CONTAINED)) 411 t.linkedPredicate("fhir:index", Integer.toString(element.getIndex()), linkResolver == null ? null : linkResolver.resolvePage("rdf.html#index")); 412 413 if ("Coding".equals(element.getType())) 414 decorateCoding(t, element, section); 415 if (Utilities.existsInList(element.getType(), "Reference")) 416 decorateReference(t, element); 417 else if (Utilities.existsInList(element.getType(), "canonical")) 418 decorateCanonical(t, element); 419 420 if("canonical".equals(element.getType())) { 421 String refURI = element.primitiveValue(); 422 if (refURI != null) { 423 String uriType = getURIType(refURI); 424 if(uriType != null && !section.hasSubject(refURI)) 425 section.triple(refURI, "a", "fhir:" + uriType); 426 } 427 } 428 429 if("Reference".equals(element.getType())) { 430 String refURI = getReferenceURI(element.getChildValue("reference")); 431 if (refURI != null) { 432 String uriType = getURIType(refURI); 433 if(uriType != null && !section.hasSubject(refURI)) 434 section.triple(refURI, "a", "fhir:" + uriType); 435 } 436 } 437 438 for (Element child : element.getChildren()) { 439 if ("xhtml".equals(child.getType())) { 440 String childfn = getFormalName(child); 441 t.predicate("fhir:" + childfn, ttlLiteral(child.getValue(), child.getType())); 442 } else 443 composeElement(section, t, child, element); 444 } 445 } 446 447 private String getFormalName(Element element) { 448 String en = null; 449 if (element.getSpecial() == null) { 450 if (element.getProperty().getDefinition().hasBase()) 451 en = element.getProperty().getDefinition().getBase().getPath(); 452 } 453 else if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY) 454 en = "Bundle.entry.resource"; 455 else if (element.getSpecial() == SpecialElement.BUNDLE_OUTCOME) 456 en = "Bundle.entry.response.outcome"; 457 else if (element.getSpecial() == SpecialElement.PARAMETER) 458 en = element.getElementProperty().getDefinition().getPath(); 459 else // CONTAINED 460 en = "DomainResource.contained"; 461 462 if (en == null) 463 en = element.getProperty().getDefinition().getPath(); 464 boolean doType = false; 465 if (en.endsWith("[x]")) { 466 en = en.substring(0, en.length()-3); 467 doType = true; 468 } 469 if (doType || (element.getProperty().getDefinition().getType().size() > 1 && !allReference(element.getProperty().getDefinition().getType()))) 470 en = en + Utilities.capitalize(element.getType()); 471 return en; 472 } 473 474 private boolean allReference(List<TypeRefComponent> types) { 475 for (TypeRefComponent t : types) { 476 if (!t.getCode().equals("Reference")) 477 return false; 478 } 479 return true; 480 } 481 482 static public String ttlLiteral(String value, String type) { 483 String xst = ""; 484 if (type.equals("boolean")) 485 xst = "^^xsd:boolean"; 486 else if (type.equals("integer")) 487 xst = "^^xsd:integer"; 488 else if (type.equals("integer64")) 489 xst = "^^xsd:long"; 490 else if (type.equals("unsignedInt")) 491 xst = "^^xsd:nonNegativeInteger"; 492 else if (type.equals("positiveInt")) 493 xst = "^^xsd:positiveInteger"; 494 else if (type.equals("decimal")) 495 xst = "^^xsd:decimal"; 496 else if (type.equals("base64Binary")) 497 xst = "^^xsd:base64Binary"; 498 else if (type.equals("instant")) 499 xst = "^^xsd:dateTime"; 500 else if (type.equals("time")) 501 xst = "^^xsd:time"; 502 else if (type.equals("date") || type.equals("dateTime") ) { 503 String v = value; 504 if (v.length() > 10) { 505 int i = value.substring(10).indexOf("-"); 506 if (i == -1) 507 i = value.substring(10).indexOf("+"); 508 v = i == -1 ? value : value.substring(0, 10+i); 509 } 510 if (v.length() > 10) 511 xst = "^^xsd:dateTime"; 512 else if (v.length() == 10) 513 xst = "^^xsd:date"; 514 else if (v.length() == 7) 515 xst = "^^xsd:gYearMonth"; 516 else if (v.length() == 4) 517 xst = "^^xsd:gYear"; 518 } 519 520 return "\"" +Turtle.escape(value, true) + "\""+xst; 521 } 522 523 protected void decorateCoding(Complex t, Element coding, Section section) throws FHIRException { 524 String system = coding.getChildValue("system"); 525 String code = coding.getChildValue("code"); 526 527 if (system == null || code == null) 528 return; 529 if ("http://snomed.info/sct".equals(system)) { 530 t.prefix("sct", "http://snomed.info/id/"); 531 if (code.contains(":") || code.contains("=")) 532 generateLinkedPredicate(t, code); 533 else 534 t.linkedPredicate("a", "sct:" + urlescape(code), null); 535 } else if ("http://loinc.org".equals(system)) { 536 t.prefix("loinc", "http://loinc.org/rdf#"); 537 t.linkedPredicate("a", "loinc:"+urlescape(code).toUpperCase(), null); 538 } 539 } 540 private void generateLinkedPredicate(Complex t, String code) throws FHIRException { 541 Expression expression = SnomedExpressions.parse(code); 542 543 } 544 545 546// 128045006|cellulitis (disorder)|:{363698007|finding site|=56459004|foot structure|} 547// Grahame Grieve: or 548// 549// 64572001|disease|:{116676008|associated morphology|=72704001|fracture|,363698007|finding site|=(12611008|bone structure of tibia|:272741003|laterality|=7771000|left|)} 550// Harold Solbrig: 551// a sct:128045006, 552// rdfs:subClassOf [ 553// a owl:Restriction; 554// owl:onProperty sct:609096000 ; 555// owl:someValuesFrom [ 556// a owl:Restriction; 557// owl:onProperty sct:363698007 ; 558// owl:someValuesFrom sct:56459004 ] ] ; 559// and 560// 561// a sct:64572001, 562// rdfs:subclassOf [ 563// a owl:Restriction ; 564// owl:onProperty sct:60909600 ; 565// owl:someValuesFrom [ 566// a owl:Class ; 567// owl:intersectionOf ( [ 568// a owl:Restriction; 569// owl:onProperty sct:116676008; 570// owl:someValuesFrom sct:72704001 ] 571// [ a owl:Restriction; 572// owl:onProperty sct:363698007 573// owl:someValuesFrom [ 574// a owl:Class ; 575// owl:intersectionOf( 576// sct:12611008 577// owl:someValuesFrom [ 578// a owl:Restriction; 579// owl:onProperty sct:272741003; 580// owl:someValuesFrom sct:7771000 581// ] ) ] ] ) ] ] 582// (an approximation -- I'll have to feed it into a translator to be sure I've got it 100% right) 583// 584 585}