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