001package org.hl7.fhir.dstu2016may.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.apache.commons.lang3.StringUtils;
039import org.hl7.fhir.dstu2016may.model.BooleanType;
040import org.hl7.fhir.dstu2016may.model.CodeSystem;
041import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
042import org.hl7.fhir.dstu2016may.model.CodeType;
043import org.hl7.fhir.dstu2016may.model.CodeableConcept;
044import org.hl7.fhir.dstu2016may.model.Coding;
045import org.hl7.fhir.dstu2016may.model.DataElement;
046import org.hl7.fhir.dstu2016may.model.DomainResource;
047import org.hl7.fhir.dstu2016may.model.Element;
048import org.hl7.fhir.dstu2016may.model.ElementDefinition;
049import org.hl7.fhir.dstu2016may.model.Extension;
050import org.hl7.fhir.dstu2016may.model.ExtensionHelper;
051import org.hl7.fhir.dstu2016may.model.Factory;
052import org.hl7.fhir.dstu2016may.model.Identifier;
053import org.hl7.fhir.dstu2016may.model.IntegerType;
054import org.hl7.fhir.dstu2016may.model.MarkdownType;
055import org.hl7.fhir.dstu2016may.model.PrimitiveType;
056import org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemComponent;
057import org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemType;
058import org.hl7.fhir.dstu2016may.model.StringType;
059import org.hl7.fhir.dstu2016may.model.Type;
060import org.hl7.fhir.dstu2016may.model.UriType;
061import org.hl7.fhir.dstu2016may.model.ValueSet;
062import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
063import org.hl7.fhir.exceptions.FHIRFormatError;
064
065
066public class ToolingExtensions {
067
068  // validated
069  public static final String EXT_SUBSUMES = "http://hl7.org/fhir/StructureDefinition/codesystem-subsumes"; 
070  private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
071//  public static final String EXT_DEPRECATED = "http://hl7.org/fhir/StructureDefinition/codesystem-deprecated";
072  public static final String EXT_DEFINITION = "http://hl7.org/fhir/StructureDefinition/valueset-definition";
073  public static final String EXT_COMMENT = "http://hl7.org/fhir/StructureDefinition/valueset-comments";
074  private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier";
075  private static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation";
076  public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source";
077  public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint"; 
078  public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
079  public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type"; 
080  public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type"; 
081  public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/structuredefinition-regex"; 
082  public static final String EXT_CONTROL = "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl"; 
083  public static final String EXT_MINOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs"; 
084  public static final String EXT_MAXOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs";
085  public static final String EXT_ALLOWEDRESOURCE = "http://hl7.org/fhir/StructureDefinition/questionnaire-allowedResource";
086  public static final String EXT_REFERENCEFILTER = "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceFilter";
087
088  // unregistered?
089
090//  public static final String EXT_FLYOVER = "http://hl7.org/fhir/Profile/questionnaire-extensions#flyover";
091//  private static final String EXT_QTYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
092//  private static final String EXT_QREF = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
093//  private static final String EXTENSION_FILTER_ONLY = "http://www.healthintersections.com.au/fhir/Profile/metadata#expandNeedsFilter";
094//  private static final String EXT_TYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
095//  private static final String EXT_REFERENCE = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
096  private static final String EXT_FHIRTYPE = "http://hl7.org/fhir/StructureDefinition/questionnaire-fhirType";
097  private static final String EXT_ALLOWABLE_UNITS = "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits";
098  public static final String EXT_CIMI_REFERENCE = "http://hl7.org/fhir/StructureDefinition/cimi-reference";
099  public static final String EXT_UNCLOSED = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed";
100  public static final String EXT_FMM_LEVEL = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm";
101
102
103  // specific extension helpers
104
105  public static Extension makeIssueSource(Source source) {
106    Extension ex = new Extension();
107    // todo: write this up and get it published with the pack (and handle the redirect?)
108    ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE);
109    CodeType c = new CodeType();
110    c.setValue(source.toString());
111    ex.setValue(c);
112    return ex;
113  }
114
115  public static boolean hasExtension(DomainResource de, String url) {
116    return getExtension(de, url) != null;
117  }
118
119  public static boolean hasExtension(Element e, String url) {
120    return getExtension(e, url) != null;
121  }
122
123  public static void addStringExtension(DomainResource dr, String url, String content) {
124    if (!StringUtils.isBlank(content)) {
125      Extension ex = getExtension(dr, url);
126      if (ex != null)
127        ex.setValue(new StringType(content));
128      else
129        dr.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
130    }
131  }
132
133  public static void addMarkdownExtension(DomainResource dr, String url, String content) {
134    if (!StringUtils.isBlank(content)) {
135      Extension ex = getExtension(dr, url);
136      if (ex != null)
137        ex.setValue(new StringType(content));
138      else
139        dr.getExtension().add(Factory.newExtension(url, new MarkdownType(content), true));   
140    }
141  }
142
143  public static void addStringExtension(Element e, String url, String content) {
144    if (!StringUtils.isBlank(content)) {
145      Extension ex = getExtension(e, url);
146      if (ex != null)
147        ex.setValue(new StringType(content));
148      else
149        e.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
150    }
151  }
152
153  public static void addIntegerExtension(DomainResource dr, String url, int value) {
154    Extension ex = getExtension(dr, url);
155    if (ex != null)
156      ex.setValue(new IntegerType(value));
157    else
158      dr.getExtension().add(Factory.newExtension(url, new IntegerType(value), true));   
159  }
160
161  public static void addComment(Element nc, String comment) {
162    if (!StringUtils.isBlank(comment))
163      nc.getExtension().add(Factory.newExtension(EXT_COMMENT, Factory.newString_(comment), true));   
164  }
165
166//  public static void markDeprecated(Element nc) {
167//    setDeprecated(nc);   
168//  }
169//
170  public static void addSubsumes(ConceptDefinitionComponent nc, String code) {
171    nc.getModifierExtension().add(Factory.newExtension(EXT_SUBSUMES, Factory.newCode(code), true));   
172  }
173
174  public static void addDefinition(Element nc, String definition) {
175    if (!StringUtils.isBlank(definition))
176      nc.getExtension().add(Factory.newExtension(EXT_DEFINITION, Factory.newString_(definition), true));   
177  }
178
179  public static void addDisplayHint(Element def, String hint) {
180    if (!StringUtils.isBlank(hint))
181      def.getExtension().add(Factory.newExtension(EXT_DISPLAY_HINT, Factory.newString_(hint), true));   
182  }
183
184  public static String getDisplayHint(Element def) {
185    return readStringExtension(def, EXT_DISPLAY_HINT);    
186  }
187
188  public static String readStringExtension(Element c, String uri) {
189    Extension ex = ExtensionHelper.getExtension(c, uri);
190    if (ex == null)
191      return null;
192    if (ex.getValue() instanceof UriType)
193      return ((UriType) ex.getValue()).getValue();
194    if (!(ex.getValue() instanceof StringType))
195      return null;
196    return ((StringType) ex.getValue()).getValue();
197  }
198
199  public static String readStringExtension(DomainResource c, String uri) {
200    Extension ex = getExtension(c, uri);
201    if (ex == null)
202      return null;
203    if ((ex.getValue() instanceof StringType))
204      return ((StringType) ex.getValue()).getValue();
205    if ((ex.getValue() instanceof UriType))
206      return ((UriType) ex.getValue()).getValue();
207    if ((ex.getValue() instanceof MarkdownType))
208      return ((MarkdownType) ex.getValue()).getValue();
209    return null;
210  }
211
212  @SuppressWarnings("unchecked")
213  public static PrimitiveType<Type> readPrimitiveExtension(DomainResource c, String uri) {
214    Extension ex = getExtension(c, uri);
215    if (ex == null)
216      return null;
217    return (PrimitiveType<Type>) ex.getValue();
218  }
219
220  public static boolean findStringExtension(Element c, String uri) {
221    Extension ex = ExtensionHelper.getExtension(c, uri);
222    if (ex == null)
223      return false;
224    if (!(ex.getValue() instanceof StringType))
225      return false;
226    return !StringUtils.isBlank(((StringType) ex.getValue()).getValue());
227  }
228
229  public static Boolean readBooleanExtension(Element c, String uri) {
230    Extension ex = ExtensionHelper.getExtension(c, uri);
231    if (ex == null)
232      return null;
233    if (!(ex.getValue() instanceof BooleanType))
234      return null;
235    return ((BooleanType) ex.getValue()).getValue();
236  }
237
238  public static boolean findBooleanExtension(Element c, String uri) {
239    Extension ex = ExtensionHelper.getExtension(c, uri);
240    if (ex == null)
241      return false;
242    if (!(ex.getValue() instanceof BooleanType))
243      return false;
244    return true;
245  }
246
247  public static String getComment(ConceptDefinitionComponent c) {
248    return readStringExtension(c, EXT_COMMENT);    
249  }
250//
251//  public static Boolean getDeprecated(Element c) {
252//    return readBooleanExtension(c, EXT_DEPRECATED);    
253//  }
254
255  public static boolean hasComment(ConceptDefinitionComponent c) {
256    return findStringExtension(c, EXT_COMMENT);    
257  }
258
259//  public static boolean hasDeprecated(Element c) {
260//    return findBooleanExtension(c, EXT_DEPRECATED);    
261//  }
262
263  public static List<CodeType> getSubsumes(ConceptDefinitionComponent c) {
264    List<CodeType> res = new ArrayList<CodeType>();
265
266    for (Extension e : c.getExtension()) {
267      if (EXT_SUBSUMES.equals(e.getUrl()))
268        res.add((CodeType) e.getValue());
269    }
270    return res;
271  }
272
273  public static void addFlyOver(QuestionnaireItemComponent item, String text){
274    if (!StringUtils.isBlank(text)) {
275        QuestionnaireItemComponent display = item.addItem();
276        display.setType(QuestionnaireItemType.DISPLAY);
277        display.setText(text);
278        display.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept("flyover", "http://hl7.org/fhir/questionnaire-item-control", "Fly-over"), true));
279    }
280  }
281
282  public static void addMin(QuestionnaireItemComponent item, int min) {
283    item.getExtension().add(Factory.newExtension(EXT_MINOCCURS, Factory.newInteger(min), true));
284  }
285  
286  public static void addMax(QuestionnaireItemComponent item, int max) {
287    item.getExtension().add(Factory.newExtension(EXT_MAXOCCURS, Factory.newInteger(max), true));
288  }
289  
290  public static void addFhirType(QuestionnaireItemComponent group, String value) {
291    group.getExtension().add(Factory.newExtension(EXT_FHIRTYPE, Factory.newString_(value), true));       
292  }
293
294  public static void addControl(QuestionnaireItemComponent group, String value) {
295    group.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept(value, "http://hl7.org/fhir/questionnaire-item-control", value), true));
296  }
297
298  public static void addAllowedResource(QuestionnaireItemComponent group, String value) {
299    group.getExtension().add(Factory.newExtension(EXT_ALLOWEDRESOURCE, Factory.newCode(value), true));       
300  }
301
302  public static void addReferenceFilter(QuestionnaireItemComponent group, String value) {
303    group.getExtension().add(Factory.newExtension(EXT_REFERENCEFILTER, Factory.newString_(value), true));       
304  }
305
306  public static void addIdentifier(Element element, Identifier value) {
307    element.getExtension().add(Factory.newExtension(EXT_IDENTIFIER, value, true));       
308  }
309
310  /**
311   * @param name the identity of the extension of interest
312   * @return The extension, if on this element, else null
313   */
314  public static Extension getExtension(DomainResource resource, String name) {
315    if (name == null)
316      return null;
317    if (!resource.hasExtension())
318      return null;
319    for (Extension e : resource.getExtension()) {
320      if (name.equals(e.getUrl()))
321        return e;
322    }
323    return null;
324  }
325
326  public static Extension getExtension(Element el, String name) {
327    if (name == null)
328      return null;
329    if (!el.hasExtension())
330      return null;
331    for (Extension e : el.getExtension()) {
332      if (name.equals(e.getUrl()))
333        return e;
334    }
335    return null;
336  }
337
338  public static void setStringExtension(DomainResource resource, String uri, String value) {
339    Extension ext = getExtension(resource, uri);
340    if (ext != null)
341      ext.setValue(new StringType(value));
342    else
343      resource.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value)));
344  }
345
346  public static String getOID(CodeSystem define) {
347    return readStringExtension(define, EXT_OID);    
348  }
349
350  public static String getOID(ValueSet vs) {
351    return readStringExtension(vs, EXT_OID);    
352  }
353
354  public static void setOID(CodeSystem define, String oid) throws FHIRFormatError, URISyntaxException {
355    if (!oid.startsWith("urn:oid:"))
356      throw new FHIRFormatError("Error in OID format");
357    if (oid.startsWith("urn:oid:urn:oid:"))
358      throw new FHIRFormatError("Error in OID format");
359    if (!hasExtension(define, EXT_OID))
360    define.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));       
361    else if (!oid.equals(readStringExtension(define, EXT_OID)))
362      throw new Error("Attempt to assign multiple OIDs to a code system");
363  }
364  public static void setOID(ValueSet vs, String oid) throws FHIRFormatError, URISyntaxException {
365    if (!oid.startsWith("urn:oid:"))
366      throw new FHIRFormatError("Error in OID format");
367    if (oid.startsWith("urn:oid:urn:oid:"))
368      throw new FHIRFormatError("Error in OID format");
369    if (!hasExtension(vs, EXT_OID))
370    vs.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));       
371    else if (!oid.equals(readStringExtension(vs, EXT_OID)))
372      throw new Error("Attempt to assign multiple OIDs to value set "+vs.getName()+" ("+vs.getUrl()+"). Has "+readStringExtension(vs, EXT_OID)+", trying to add "+oid);
373  }
374
375  public static boolean hasLanguageTranslation(Element element, String lang) {
376    for (Extension e : element.getExtension()) {
377      if (e.getUrl().equals(EXT_TRANSLATION)) {
378        Extension e1 = ExtensionHelper.getExtension(e, "lang");
379
380        if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang))
381          return true;
382      }
383    }
384    return false;
385  }
386
387  public static String getLanguageTranslation(Element element, String lang) {
388    for (Extension e : element.getExtension()) {
389      if (e.getUrl().equals(EXT_TRANSLATION)) {
390        Extension e1 = ExtensionHelper.getExtension(e, "lang");
391
392        if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) {
393          e1 = ExtensionHelper.getExtension(e, "content");
394          return ((StringType) e.getValue()).getValue();
395        }
396      }
397    }
398    return null;
399  }
400
401  public static void addLanguageTranslation(Element element, String lang, String value) {
402    Extension extension = new Extension().setUrl(EXT_TRANSLATION);
403    extension.addExtension().setUrl("lang").setValue(new StringType(lang));
404    extension.addExtension().setUrl("content").setValue(new StringType(value));
405    element.getExtension().add(extension);
406  }
407
408  public static Type getAllowedUnits(ElementDefinition eld) {
409    for (Extension e : eld.getExtension()) 
410      if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) 
411        return e.getValue();
412    return null;
413  }
414
415  public static void setAllowableUnits(ElementDefinition eld, CodeableConcept cc) {
416    for (Extension e : eld.getExtension()) 
417      if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) {
418        e.setValue(cc);
419        return;
420      }
421    eld.getExtension().add(new Extension().setUrl(EXT_ALLOWABLE_UNITS).setValue(cc));
422  }
423
424  public static List<Extension> getExtensions(Element element, String url) {
425    List<Extension> results = new ArrayList<Extension>();
426    for (Extension ex : element.getExtension())
427      if (ex.getUrl().equals(url))
428        results.add(ex);
429    return results;
430  }
431
432  public static List<Extension> getExtensions(DomainResource resource, String url) {
433    List<Extension> results = new ArrayList<Extension>();
434    for (Extension ex : resource.getExtension())
435      if (ex.getUrl().equals(url))
436        results.add(ex);
437    return results;
438  }
439
440  public static void addDEReference(DataElement de, String value) {
441    for (Extension e : de.getExtension()) 
442      if (e.getUrl().equals(EXT_CIMI_REFERENCE)) {
443        e.setValue(new UriType(value));
444        return;
445      }
446    de.getExtension().add(new Extension().setUrl(EXT_CIMI_REFERENCE).setValue(new UriType(value)));
447  }
448
449//  public static void setDeprecated(Element nc) {
450//    for (Extension e : nc.getExtension()) 
451//      if (e.getUrl().equals(EXT_DEPRECATED)) {
452//        e.setValue(new BooleanType(true));
453//        return;
454//      }
455//    nc.getExtension().add(new Extension().setUrl(EXT_DEPRECATED).setValue(new BooleanType(true)));    
456//  }
457
458  public static void setExtension(Element focus, String url, Coding c) {
459    for (Extension e : focus.getExtension()) 
460      if (e.getUrl().equals(url)) {
461        e.setValue(c);
462        return;
463      }
464    focus.getExtension().add(new Extension().setUrl(url).setValue(c));    
465  }
466
467  public static void removeExtension(DomainResource focus, String url) {
468    Iterator<Extension> i = focus.getExtension().iterator();
469    while (i.hasNext()) {
470      Extension e = i.next(); // must be called before you can call i.remove()
471      if (e.getUrl().equals(url)) {
472        i.remove();
473      }
474    }
475  }
476  
477  public static void removeExtension(Element focus, String url) {
478    Iterator<Extension> i = focus.getExtension().iterator();
479    while (i.hasNext()) {
480      Extension e = i.next(); // must be called before you can call i.remove()
481      if (e.getUrl().equals(url)) {
482        i.remove();
483      }
484    }
485  }
486
487  public static boolean hasOID(ValueSet vs) {
488    return hasExtension(vs, EXT_OID);
489  }
490  
491
492  public static void setStringExtension(Element element, String uri, String value) {
493    Extension ext = getExtension(element, uri);
494    if (ext != null)
495      ext.setValue(new StringType(value));
496    else
497      element.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value)));
498  }
499  
500}