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