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