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