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