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