001package org.hl7.fhir.instance.utils;
002
003import java.net.URISyntaxException;
004
005/*
006Copyright (c) 2011+, HL7, Inc
007All rights reserved.
008
009Redistribution and use in source and binary forms, with or without modification, 
010are 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
021THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
022ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
023WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
024IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
025INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
026NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
027PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
028WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
029ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
030POSSIBILITY OF SUCH DAMAGE.
031
032 */
033
034import java.util.ArrayList;
035import java.util.Iterator;
036import java.util.List;
037
038import org.hl7.fhir.instance.model.BooleanType;
039import org.hl7.fhir.instance.model.CodeType;
040import org.hl7.fhir.instance.model.CodeableConcept;
041import org.hl7.fhir.instance.model.Coding;
042import org.hl7.fhir.instance.model.DataElement;
043import org.hl7.fhir.instance.model.DomainResource;
044import org.hl7.fhir.instance.model.Element;
045import org.hl7.fhir.instance.model.ElementDefinition;
046import org.hl7.fhir.instance.model.Extension;
047import org.hl7.fhir.instance.model.ExtensionHelper;
048import org.hl7.fhir.instance.model.Factory;
049import org.hl7.fhir.instance.model.Identifier;
050import org.hl7.fhir.instance.model.IntegerType;
051import org.hl7.fhir.instance.model.MarkdownType;
052import org.hl7.fhir.instance.model.PrimitiveType;
053import org.hl7.fhir.instance.model.Questionnaire.GroupComponent;
054import org.hl7.fhir.instance.model.Questionnaire.QuestionComponent;
055import org.hl7.fhir.instance.model.Reference;
056import org.hl7.fhir.instance.model.StringType;
057import org.hl7.fhir.instance.model.Type;
058import org.hl7.fhir.instance.model.UriType;
059import org.hl7.fhir.instance.model.ValueSet;
060import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
061import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent;
062import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
063import org.hl7.fhir.exceptions.FHIRFormatError;
064import org.hl7.fhir.utilities.Utilities;
065
066
067public class ToolingExtensions {
068
069  // validated
070  public static final String EXT_SUBSUMES = "http://hl7.org/fhir/StructureDefinition/valueset-subsumes"; 
071  private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
072  public static final String EXT_DEPRECATED = "http://hl7.org/fhir/StructureDefinition/valueset-deprecated";
073  public static final String EXT_DEFINITION = "http://hl7.org/fhir/StructureDefinition/valueset-definition";
074  public static final String EXT_COMMENT = "http://hl7.org/fhir/StructureDefinition/valueset-comments";
075  private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier";
076  private static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation";
077  public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source";
078  public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint"; 
079  public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
080  public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type"; 
081  public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type"; 
082  public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/structuredefinition-regex"; 
083  public static final String EXT_EXPRESSION = "http://hl7.org/fhir/StructureDefinition/structuredefinition-expression";
084  public static final String EXT_SEARCH_EXPRESSION = "http://hl7.org/fhir/StructureDefinition/searchparameter-expression";
085
086  // unregistered?
087
088  public static final String EXT_FLYOVER = "http://hl7.org/fhir/Profile/questionnaire-extensions#flyover";
089  private static final String EXT_QTYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
090  private static final String EXT_QREF = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
091  private static final String EXTENSION_FILTER_ONLY = "http://www.healthintersections.com.au/fhir/Profile/metadata#expandNeedsFilter";
092  private static final String EXT_TYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
093  private static final String EXT_REFERENCE = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
094  private static final String EXT_ALLOWABLE_UNITS = "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits";
095  public static final String EXT_CIMI_REFERENCE = "http://hl7.org/fhir/StructureDefinition/cimi-reference";
096  public static final String EXT_UNCLOSED = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed";
097  public static final String EXT_FMM_LEVEL = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm";
098
099
100  // specific extension helpers
101
102  public static Extension makeIssueSource(Source source) {
103    Extension ex = new Extension();
104    // todo: write this up and get it published with the pack (and handle the redirect?)
105    ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE);
106    CodeType c = new CodeType();
107    c.setValue(source.toString());
108    ex.setValue(c);
109    return ex;
110  }
111
112  public static boolean hasExtension(DomainResource de, String url) {
113    return getExtension(de, url) != null;
114  }
115
116  public static boolean hasExtension(Element e, String url) {
117    return getExtension(e, url) != null;
118  }
119
120  public static void addStringExtension(DomainResource dr, String url, String content) {
121    if (!Utilities.noString(content)) {
122      Extension ex = getExtension(dr, url);
123      if (ex != null)
124        ex.setValue(new StringType(content));
125      else
126        dr.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
127    }
128  }
129
130  public static void addStringExtension(Element e, String url, String content) {
131    if (!Utilities.noString(content)) {
132      Extension ex = getExtension(e, url);
133      if (ex != null)
134        ex.setValue(new StringType(content));
135      else
136        e.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
137    }
138  }
139
140  public static void addIntegerExtension(DomainResource dr, String url, int value) {
141    Extension ex = getExtension(dr, url);
142    if (ex != null)
143      ex.setValue(new IntegerType(value));
144    else
145      dr.getExtension().add(Factory.newExtension(url, new IntegerType(value), true));   
146  }
147
148  public static void addComment(Element nc, String comment) {
149    if (!Utilities.noString(comment))
150      nc.getExtension().add(Factory.newExtension(EXT_COMMENT, Factory.newString_(comment), true));   
151  }
152
153  public static void markDeprecated(Element nc) {
154    setDeprecated(nc);   
155  }
156
157  public static void addSubsumes(ConceptDefinitionComponent nc, String code) {
158    nc.getModifierExtension().add(Factory.newExtension(EXT_SUBSUMES, Factory.newCode(code), true));   
159  }
160
161  public static void addDefinition(Element nc, String definition) {
162    if (!Utilities.noString(definition))
163      nc.getExtension().add(Factory.newExtension(EXT_DEFINITION, Factory.newString_(definition), true));   
164  }
165
166  public static void addDisplayHint(Element def, String hint) {
167    if (!Utilities.noString(hint))
168      def.getExtension().add(Factory.newExtension(EXT_DISPLAY_HINT, Factory.newString_(hint), true));   
169  }
170
171  public static String getDisplayHint(Element def) {
172    return readStringExtension(def, EXT_DISPLAY_HINT);    
173  }
174
175  public static String readStringExtension(Element c, String uri) {
176    Extension ex = ExtensionHelper.getExtension(c, uri);
177    if (ex == null)
178      return null;
179    if (ex.getValue() instanceof UriType)
180      return ((UriType) ex.getValue()).getValue();
181    if (!(ex.getValue() instanceof StringType))
182      return null;
183    return ((StringType) ex.getValue()).getValue();
184  }
185
186  public static String readStringExtension(DomainResource c, String uri) {
187    Extension ex = getExtension(c, uri);
188    if (ex == null)
189      return null;
190    if ((ex.getValue() instanceof StringType))
191      return ((StringType) ex.getValue()).getValue();
192    if ((ex.getValue() instanceof UriType))
193      return ((UriType) ex.getValue()).getValue();
194    if ((ex.getValue() instanceof MarkdownType))
195      return ((MarkdownType) ex.getValue()).getValue();
196    return null;
197  }
198
199  @SuppressWarnings("unchecked")
200  public static PrimitiveType<Type> readPrimitiveExtension(DomainResource c, String uri) {
201    Extension ex = getExtension(c, uri);
202    if (ex == null)
203      return null;
204    return (PrimitiveType<Type>) ex.getValue();
205  }
206
207  public static boolean findStringExtension(Element c, String uri) {
208    Extension ex = ExtensionHelper.getExtension(c, uri);
209    if (ex == null)
210      return false;
211    if (!(ex.getValue() instanceof StringType))
212      return false;
213    return !Utilities.noString(((StringType) ex.getValue()).getValue());
214  }
215
216  public static Boolean readBooleanExtension(Element c, String uri) {
217    Extension ex = ExtensionHelper.getExtension(c, uri);
218    if (ex == null)
219      return null;
220    if (!(ex.getValue() instanceof BooleanType))
221      return null;
222    return ((BooleanType) ex.getValue()).getValue();
223  }
224
225  public static boolean findBooleanExtension(Element c, String uri) {
226    Extension ex = ExtensionHelper.getExtension(c, uri);
227    if (ex == null)
228      return false;
229    if (!(ex.getValue() instanceof BooleanType))
230      return false;
231    return true;
232  }
233
234  public static String getComment(ConceptDefinitionComponent c) {
235    return readStringExtension(c, EXT_COMMENT);    
236  }
237
238  public static Boolean getDeprecated(Element c) {
239    return readBooleanExtension(c, EXT_DEPRECATED);    
240  }
241
242  public static boolean hasComment(ConceptDefinitionComponent c) {
243    return findStringExtension(c, EXT_COMMENT);    
244  }
245
246  public static boolean hasDeprecated(Element c) {
247    return findBooleanExtension(c, EXT_DEPRECATED);    
248  }
249
250  public static List<CodeType> getSubsumes(ConceptDefinitionComponent c) {
251    List<CodeType> res = new ArrayList<CodeType>();
252
253    for (Extension e : c.getExtension()) {
254      if (EXT_SUBSUMES.equals(e.getUrl()))
255        res.add((CodeType) e.getValue());
256    }
257    return res;
258  }
259
260  public static void addFlyOver(GroupComponent group, String text) {
261    if (!Utilities.noString(text))
262      group.getExtension().add(Factory.newExtension(EXT_FLYOVER, Factory.newString_(text), true));   
263
264  }
265
266  public static void setQuestionType(GroupComponent group, String text) {
267    if (!Utilities.noString(text))
268      group.getExtension().add(Factory.newExtension(EXT_QTYPE, Factory.newString_(text), true));   
269  }
270
271  public static void setQuestionReference(GroupComponent group, String text) {
272    if (!Utilities.noString(text))
273      group.getExtension().add(Factory.newExtension(EXT_QREF, Factory.newString_(text), true));   
274  }
275
276  public static void addFlyOver(Element element, String text) {
277    element.getExtension().add(Factory.newExtension(EXT_FLYOVER, Factory.newString_(text), true));       
278  }
279
280  public static void addFilterOnly(Reference element, boolean value) {
281    element.getExtension().add(Factory.newExtension(EXTENSION_FILTER_ONLY, Factory.newBoolean(value), true));       
282  }
283
284  public static void addType(GroupComponent group, String value) {
285    group.getExtension().add(Factory.newExtension(EXT_TYPE, Factory.newString_(value), true));       
286  }
287
288  public static void addReference(QuestionComponent group, String value) {
289    group.getExtension().add(Factory.newExtension(EXT_REFERENCE, Factory.newString_(value), true));       
290  }
291
292  public static void addIdentifier(Element element, Identifier value) {
293    element.getExtension().add(Factory.newExtension(EXT_IDENTIFIER, value, true));       
294  }
295
296  /**
297   * @param name the identity of the extension of interest
298   * @return The extension, if on this element, else null
299   */
300  public static Extension getExtension(DomainResource resource, String name) {
301    if (name == null)
302      return null;
303    if (!resource.hasExtension())
304      return null;
305    for (Extension e : resource.getExtension()) {
306      if (name.equals(e.getUrl()))
307        return e;
308    }
309    return null;
310  }
311
312  public static Extension getExtension(Element el, String name) {
313    if (name == null)
314      return null;
315    if (!el.hasExtension())
316      return null;
317    for (Extension e : el.getExtension()) {
318      if (name.equals(e.getUrl()))
319        return e;
320    }
321    return null;
322  }
323
324  public static void setStringExtension(DomainResource resource, String uri, String value) {
325    Extension ext = getExtension(resource, uri);
326    if (ext != null)
327      ext.setValue(new StringType(value));
328    else
329      resource.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value)));
330  }
331
332  public static String getOID(ValueSetCodeSystemComponent define) {
333    return readStringExtension(define, EXT_OID);    
334  }
335
336  public static String getOID(ValueSet vs) {
337    return readStringExtension(vs, EXT_OID);    
338  }
339
340  public static void setOID(ValueSetCodeSystemComponent define, String oid) throws FHIRFormatError, URISyntaxException {
341    if (!oid.startsWith("urn:oid:"))
342      throw new FHIRFormatError("Error in OID format");
343    if (oid.startsWith("urn:oid:urn:oid:"))
344      throw new FHIRFormatError("Error in OID format");
345    define.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));       
346  }
347  public static void setOID(ValueSet vs, String oid) throws FHIRFormatError, URISyntaxException {
348    if (!oid.startsWith("urn:oid:"))
349      throw new FHIRFormatError("Error in OID format");
350    if (oid.startsWith("urn:oid:urn:oid:"))
351      throw new FHIRFormatError("Error in OID format");
352    vs.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));       
353  }
354
355  public static boolean hasLanguageTranslation(Element element, String lang) {
356    for (Extension e : element.getExtension()) {
357      if (e.getUrl().equals(EXT_TRANSLATION)) {
358        Extension e1 = ExtensionHelper.getExtension(e, "lang");
359
360        if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang))
361          return true;
362      }
363    }
364    return false;
365  }
366
367  public static String getLanguageTranslation(Element element, String lang) {
368    for (Extension e : element.getExtension()) {
369      if (e.getUrl().equals(EXT_TRANSLATION)) {
370        Extension e1 = ExtensionHelper.getExtension(e, "lang");
371
372        if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) {
373          e1 = ExtensionHelper.getExtension(e, "content");
374          return ((StringType) e.getValue()).getValue();
375        }
376      }
377    }
378    return null;
379  }
380
381  public static void addLanguageTranslation(Element element, String lang, String value) {
382    Extension extension = new Extension().setUrl(EXT_TRANSLATION);
383    extension.addExtension().setUrl("lang").setValue(new StringType(lang));
384    extension.addExtension().setUrl("content").setValue(new StringType(value));
385    element.getExtension().add(extension);
386  }
387
388  public static Type getAllowedUnits(ElementDefinition eld) {
389    for (Extension e : eld.getExtension()) 
390      if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) 
391        return e.getValue();
392    return null;
393  }
394
395  public static void setAllowableUnits(ElementDefinition eld, CodeableConcept cc) {
396    for (Extension e : eld.getExtension()) 
397      if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) {
398        e.setValue(cc);
399        return;
400      }
401    eld.getExtension().add(new Extension().setUrl(EXT_ALLOWABLE_UNITS).setValue(cc));
402  }
403
404  public static List<Extension> getExtensions(Element element, String url) {
405    List<Extension> results = new ArrayList<Extension>();
406    for (Extension ex : element.getExtension())
407      if (ex.getUrl().equals(url))
408        results.add(ex);
409    return results;
410  }
411
412  public static List<Extension> getExtensions(DomainResource resource, String url) {
413    List<Extension> results = new ArrayList<Extension>();
414    for (Extension ex : resource.getExtension())
415      if (ex.getUrl().equals(url))
416        results.add(ex);
417    return results;
418  }
419
420  public static void addDEReference(DataElement de, String value) {
421    for (Extension e : de.getExtension()) 
422      if (e.getUrl().equals(EXT_CIMI_REFERENCE)) {
423        e.setValue(new UriType(value));
424        return;
425      }
426    de.getExtension().add(new Extension().setUrl(EXT_CIMI_REFERENCE).setValue(new UriType(value)));
427  }
428
429  public static void setDeprecated(Element nc) {
430    for (Extension e : nc.getExtension()) 
431      if (e.getUrl().equals(EXT_DEPRECATED)) {
432        e.setValue(new BooleanType(true));
433        return;
434      }
435    nc.getExtension().add(new Extension().setUrl(EXT_DEPRECATED).setValue(new BooleanType(true)));    
436  }
437
438  public static void setExtension(Element focus, String url, Coding c) {
439    for (Extension e : focus.getExtension()) 
440      if (e.getUrl().equals(url)) {
441        e.setValue(c);
442        return;
443      }
444    focus.getExtension().add(new Extension().setUrl(url).setValue(c));    
445  }
446
447  public static void removeExtension(DomainResource focus, String url) {
448    Iterator<Extension> i = focus.getExtension().iterator();
449    while (i.hasNext()) {
450      Extension e = i.next(); // must be called before you can call i.remove()
451      if (e.getUrl().equals(url)) {
452        i.remove();
453      }
454    }
455  }
456  
457  public static void removeExtension(Element focus, String url) {
458    Iterator<Extension> i = focus.getExtension().iterator();
459    while (i.hasNext()) {
460      Extension e = i.next(); // must be called before you can call i.remove()
461      if (e.getUrl().equals(url)) {
462        i.remove();
463      }
464    }
465  }
466  
467
468  public static void setStringExtension(Element element, String uri, String value) {
469    Extension ext = getExtension(element, uri);
470    if (ext != null)
471      ext.setValue(new StringType(value));
472    else
473      element.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value)));
474  }
475
476
477}