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