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