001package org.hl7.fhir.r4b.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
032
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.HashMap;
065import java.util.Iterator;
066import java.util.List;
067import java.util.Map;
068
069import org.apache.commons.lang3.StringUtils;
070import org.fhir.ucum.Utilities;
071import org.hl7.fhir.exceptions.FHIRException;
072import org.hl7.fhir.r4b.model.Base;
073import org.hl7.fhir.r4b.model.BooleanType;
074import org.hl7.fhir.r4b.model.CanonicalResource;
075import org.hl7.fhir.r4b.model.CanonicalType;
076import org.hl7.fhir.r4b.model.CodeSystem.ConceptDefinitionComponent;
077import org.hl7.fhir.r4b.model.CodeType;
078import org.hl7.fhir.r4b.model.CodeableConcept;
079import org.hl7.fhir.r4b.model.Coding;
080import org.hl7.fhir.r4b.model.DataType;
081import org.hl7.fhir.r4b.model.DecimalType;
082import org.hl7.fhir.r4b.model.DomainResource;
083import org.hl7.fhir.r4b.model.Element;
084import org.hl7.fhir.r4b.model.ElementDefinition;
085import org.hl7.fhir.r4b.model.Extension;
086import org.hl7.fhir.r4b.model.ExtensionHelper;
087import org.hl7.fhir.r4b.model.Factory;
088import org.hl7.fhir.r4b.model.Identifier;
089import org.hl7.fhir.r4b.model.Integer64Type;
090import org.hl7.fhir.r4b.model.IntegerType;
091import org.hl7.fhir.r4b.model.MarkdownType;
092import org.hl7.fhir.r4b.model.OperationOutcome.OperationOutcomeIssueComponent;
093import org.hl7.fhir.r4b.model.PrimitiveType;
094import org.hl7.fhir.r4b.model.Property;
095import org.hl7.fhir.r4b.model.Questionnaire.QuestionnaireItemComponent;
096import org.hl7.fhir.r4b.model.Questionnaire.QuestionnaireItemType;
097import org.hl7.fhir.r4b.model.StringType;
098import org.hl7.fhir.r4b.model.UriType;
099import org.hl7.fhir.r4b.model.UrlType;
100import org.hl7.fhir.r4b.model.ValueSet.ConceptReferenceComponent;
101import org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent;
102import org.hl7.fhir.utilities.StandardsStatus;
103import org.hl7.fhir.utilities.validation.ValidationMessage;
104import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
105import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
106import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
107
108
109public class ToolingExtensions {
110
111  // validated
112//  private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
113//  public static final String EXT_DEPRECATED = "http://hl7.org/fhir/StructureDefinition/codesystem-deprecated";
114  public static final String EXT_DEFINITION = "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition";
115  public static final String EXT_CS_COMMENT = "http://hl7.org/fhir/StructureDefinition/codesystem-concept-comments";
116  public static final String EXT_VS_COMMENT = "http://hl7.org/fhir/StructureDefinition/valueset-concept-comments";
117  public static final String EXT_CS_KEYWORD = "http://hl7.org/fhir/StructureDefinition/codesystem-keyWord";
118  public static final String EXT_VS_KEYWORD = "http://hl7.org/fhir/StructureDefinition/valueset-keyWord";
119  private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier";
120  public static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation";
121  public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source";
122  public static final String EXT_ISSUE_LINE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line";
123  public static final String EXT_ISSUE_COL = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col";
124  public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint"; 
125  public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
126  public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/regex"; 
127  public static final String EXT_CONTROL = "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl"; 
128  public static final String EXT_MINOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs"; 
129  public static final String EXT_MAXOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs";
130  public static final String EXT_ALLOWEDRESOURCE = "http://hl7.org/fhir/StructureDefinition/questionnaire-allowedResource";
131  public static final String EXT_REFERENCEFILTER = "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceFilter";
132  public static final String EXT_CODE_GENERATION_PARENT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-codegen-super";
133  public static final String EXT_HIERARCHY = "http://hl7.org/fhir/StructureDefinition/structuredefinition-hierarchy";
134  public static final String EXT_BEST_PRACTICE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice";
135  public static final String EXT_BEST_PRACTICE_EXPLANATION = "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice-explanation";
136  // unregistered?
137  public static final String EXT_MAPPING_PREFIX = "http://hl7.org/fhir/tools/StructureDefinition/logical-mapping-prefix";
138  public static final String EXT_MAPPING_SUFFIX = "http://hl7.org/fhir/tools/StructureDefinition/logical-mapping-suffix";
139
140//  public static final String EXT_FLYOVER = "http://hl7.org/fhir/Profile/questionnaire-extensions#flyover";
141  public static final String EXT_QTYPE = "http://hl7.org/fhir/StructureDefinition/questionnnaire-baseType";
142//  private static final String EXT_QREF = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
143//  private static final String EXTENSION_FILTER_ONLY = "http://www.healthintersections.com.au/fhir/Profile/metadata#expandNeedsFilter";
144//  private static final String EXT_TYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
145//  private static final String EXT_REFERENCE = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
146  private static final String EXT_FHIRTYPE = "http://hl7.org/fhir/StructureDefinition/questionnaire-fhirType";
147  private static final String EXT_ALLOWABLE_UNITS = "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits";
148  public static final String EXT_CIMI_REFERENCE = "http://hl7.org/fhir/StructureDefinition/cimi-reference";
149  public static final String EXT_UNCLOSED = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed";
150  public static final String EXT_FMM_LEVEL = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm";
151  public static final String EXT_FMM_SUPPORT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm-support";
152  public static final String EXT_FMM_DERIVED = "http://hl7.org/fhir/StructureDefinition/structuredefinition-conformance-derivedFrom";
153  public static final String EXT_SEC_CAT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-security-category";
154  public static final String EXT_RESOURCE_CATEGORY = "http://hl7.org/fhir/StructureDefinition/structuredefinition-category";
155  public static final String EXT_RESOURCE_INTERFACE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-interface";
156  public static final String EXT_TABLE_NAME = "http://hl7.org/fhir/StructureDefinition/structuredefinition-table-name";
157  public static final String EXT_OO_FILE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-file";
158  public static final String EXT_WORKGROUP = "http://hl7.org/fhir/StructureDefinition/structuredefinition-wg";
159  public static final String EXT_STANDARDS_STATUS = "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status";
160  public static final String EXT_NORMATIVE_VERSION = "http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version";
161  public static final String EXT_IGP_BASE = "http://hl7.org/fhir/StructureDefinition/igpublisher-res-base";
162  public static final String EXT_IGP_DEFNS = "http://hl7.org/fhir/StructureDefinition/igpublisher-res-defns";
163  public static final String EXT_IGP_FORMAT = "http://hl7.org/fhir/StructureDefinition/igpublisher-res-format";
164  public static final String EXT_IGP_SOURCE = "http://hl7.org/fhir/StructureDefinition/igpublisher-res-source";
165  public static final String EXT_IGP_VERSION = "http://hl7.org/fhir/StructureDefinition/igpublisher-res-version";
166  public static final String EXT_IGP_RESOURCES = "http://hl7.org/fhir/StructureDefinition/igpublisher-folder-resource";
167  public static final String EXT_IGP_PAGES = "http://hl7.org/fhir/StructureDefinition/igpublisher-folder-pages";
168  public static final String EXT_IGP_SPREADSHEET = "http://hl7.org/fhir/StructureDefinition/igpublisher-spreadsheet";
169  public static final String EXT_IGP_MAPPING_CSV = "http://hl7.org/fhir/StructureDefinition/igpublisher-mapping-csv";
170  public static final String EXT_IGP_BUNDLE = "http://hl7.org/fhir/StructureDefinition/igpublisher-bundle";
171  public static final String EXT_IGP_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/resource-information";
172  public static final String EXT_IGP_CONTAINED_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/contained-resource-information";
173  public static final String EXT_IGP_LOADVERSION = "http://hl7.org/fhir/StructureDefinition/igpublisher-loadversion";
174  public static final String EXT_MAX_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet";
175  public static final String EXT_MIN_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-minValueSet";
176  public static final String EXT_PROFILE_ELEMENT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element";
177  public static final String EXT_LIST_PACKAGE = "http://hl7.org/fhir/StructureDefinition/list-packageId";
178  public static final String EXT_MAPPING_NAME = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-name";
179  public static final String EXT_MAPPING_TYPE = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-type";
180  public static final String EXT_MAPPING_CARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-cardinality";
181  public static final String EXT_MAPPING_TGTTYPE = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-type";
182  public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality";
183  public static final String EXT_PRIVATE_BASE = "http://hl7.org/fhir/tools/";
184  public static final String EXT_ALLOWED_TYPE =  "http://hl7.org/fhir/StructureDefinition/operationdefinition-allowed-type";
185  public static final String EXT_FHIR_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type";
186  public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
187  public static final String EXT_RENDERED_VALUE = "http://hl7.org/fhir/StructureDefinition/rendered-value";
188  public static final String EXT_OLD_CONCEPTMAP_EQUIVALENCE = "http://hl7.org/fhir/1.0/StructureDefinition/extension-ConceptMap.element.target.equivalence";
189  public static final String EXT_EXP_TOOCOSTLY = "http://hl7.org/fhir/StructureDefinition/valueset-toocostly";
190  public static final String EXT_MUST_SUPPORT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support";
191  public static final String EXT_TRANSLATABLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable";
192  public static final String EXT_PATTERN = "http://hl7.org/fhir/StructureDefinition/elementdefinition-pattern";
193  public static final String EXT_BINDING_METHOD = "http://hl7.org/fhir/StructureDefinition/elementdefinition-binding-method";
194  public static final String EXT_XML_NAMESPACE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace";
195  public static final String EXT_XML_NAME = "http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name";
196  public static final String EXT_BINDING_STYLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-binding-style";
197  public static final String EXT_BINARY_FORMAT = "http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format";
198  public static final String EXT_TARGET_ID = "http://hl7.org/fhir/StructureDefinition/targetElement";
199  public static final String EXT_TARGET_PATH = "http://hl7.org/fhir/StructureDefinition/targetPath";
200  public static final String EXT_VALUESET_SYSTEM = "http://hl7.org/fhir/StructureDefinition/valueset-system";
201  
202  // specific extension helpers
203
204  public static Extension makeIssueSource(Source source) {
205    Extension ex = new Extension();
206    // todo: write this up and get it published with the pack (and handle the redirect?)
207    ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE);
208    CodeType c = new CodeType();
209    c.setValue(source.toString());
210    ex.setValue(c);
211    return ex;
212  }
213
214  public static boolean hasExtension(DomainResource de, String url) {
215    return getExtension(de, url) != null;
216  }
217
218  public static boolean hasExtension(Element e, String url) {
219    return getExtension(e, url) != null;
220  }
221
222//  public static void addStringExtension(DomainResource dr, String url, String content) {
223//    if (!StringUtils.isBlank(content)) {
224//      Extension ex = getExtension(dr, url);
225//      if (ex != null)
226//        ex.setValue(new StringType(content));
227//      else
228//        dr.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
229//    }
230//  }
231
232  public static void addMarkdownExtension(DomainResource dr, String url, String content) {
233    if (!StringUtils.isBlank(content)) {
234      Extension ex = getExtension(dr, url);
235      if (ex != null)
236        ex.setValue(new StringType(content));
237      else
238        dr.getExtension().add(Factory.newExtension(url, new MarkdownType(content), true));   
239    }
240  }
241
242  public static void addStringExtension(Element e, String url, String content) {
243    if (!StringUtils.isBlank(content)) {
244      Extension ex = getExtension(e, url);
245      if (ex != null)
246        ex.setValue(new StringType(content));
247      else
248        e.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
249    }
250  }
251
252  public static void addCodeExtension(Element e, String url, String content) {
253    if (!StringUtils.isBlank(content)) {
254      Extension ex = getExtension(e, url);
255      if (ex != null)
256        ex.setValue(new CodeType(content));
257      else
258        e.getExtension().add(Factory.newExtension(url, new CodeType(content), true));   
259    }
260  }
261
262  public static void addStringExtension(DomainResource e, String url, String content) {
263    if (!StringUtils.isBlank(content)) {
264      Extension ex = getExtension(e, url);
265      if (ex != null)
266        ex.setValue(new StringType(content));
267      else
268        e.getExtension().add(Factory.newExtension(url, new StringType(content), true));   
269    }
270  }
271
272
273  public static void addBooleanExtension(Element e, String url, boolean content) {
274    Extension ex = getExtension(e, url);
275    if (ex != null)
276      ex.setValue(new BooleanType(content));
277    else
278      e.getExtension().add(Factory.newExtension(url, new BooleanType(content), true));   
279  }
280
281  public static void addBooleanExtension(DomainResource e, String url, boolean content) {
282    Extension ex = getExtension(e, url);
283    if (ex != null)
284      ex.setValue(new BooleanType(content));
285    else
286      e.getExtension().add(Factory.newExtension(url, new BooleanType(content), true));   
287  }
288
289  public static void addIntegerExtension(DomainResource dr, String url, int value) {
290    Extension ex = getExtension(dr, url);
291    if (ex != null)
292      ex.setValue(new IntegerType(value));
293    else
294      dr.getExtension().add(Factory.newExtension(url, new IntegerType(value), true));   
295  }
296
297  public static void addCodeExtension(DomainResource dr, String url, String value) {
298    Extension ex = getExtension(dr, url);
299    if (ex != null)
300      ex.setValue(new CodeType(value));
301    else
302      dr.getExtension().add(Factory.newExtension(url, new CodeType(value), true));   
303  }
304
305  public static void addVSComment(ConceptSetComponent nc, String comment) {
306    if (!StringUtils.isBlank(comment))
307      nc.getExtension().add(Factory.newExtension(EXT_VS_COMMENT, Factory.newString_(comment), true));   
308  }
309  public static void addVSComment(ConceptReferenceComponent nc, String comment) {
310    if (!StringUtils.isBlank(comment))
311      nc.getExtension().add(Factory.newExtension(EXT_VS_COMMENT, Factory.newString_(comment), true));   
312  }
313
314  public static void addCSComment(ConceptDefinitionComponent nc, String comment) {
315    if (!StringUtils.isBlank(comment))
316      nc.getExtension().add(Factory.newExtension(EXT_CS_COMMENT, Factory.newString_(comment), true));   
317  }
318
319//  public static void markDeprecated(Element nc) {
320//    setDeprecated(nc);   
321//  }
322//
323
324  public static void addDefinition(Element nc, String definition) {
325    if (!StringUtils.isBlank(definition))
326      nc.getExtension().add(Factory.newExtension(EXT_DEFINITION, Factory.newString_(definition), true));   
327  }
328
329  public static void addDisplayHint(Element def, String hint) {
330    if (!StringUtils.isBlank(hint))
331      def.getExtension().add(Factory.newExtension(EXT_DISPLAY_HINT, Factory.newString_(hint), true));   
332  }
333
334  public static String getDisplayHint(Element def) {
335    return readStringExtension(def, EXT_DISPLAY_HINT);    
336  }
337
338  public static String readStringExtension(Element c, String uri) {
339    Extension ex = ExtensionHelper.getExtension(c, uri);
340    if (ex == null)
341      return null;
342    if (ex.getValue() instanceof UriType)
343      return ((UriType) ex.getValue()).getValue();
344    if (ex.getValue() instanceof CanonicalType)
345      return ((CanonicalType) ex.getValue()).getValue();
346    if (ex.getValue() instanceof CodeType)
347      return ((CodeType) ex.getValue()).getValue();
348    if (ex.getValue() instanceof IntegerType)
349      return ((IntegerType) ex.getValue()).asStringValue();
350    if (ex.getValue() instanceof Integer64Type)
351      return ((Integer64Type) ex.getValue()).asStringValue();
352    if (ex.getValue() instanceof DecimalType)
353      return ((DecimalType) ex.getValue()).asStringValue();
354    if ((ex.getValue() instanceof MarkdownType))
355      return ((MarkdownType) ex.getValue()).getValue();
356    if ((ex.getValue() instanceof PrimitiveType))
357      return ((PrimitiveType) ex.getValue()).primitiveValue();
358    if (!(ex.getValue() instanceof StringType))
359      return null;
360    return ((StringType) ex.getValue()).getValue();
361  }
362
363  public static String readStringExtension(DomainResource c, String uri) {
364    Extension ex = getExtension(c, uri);
365    if (ex == null)
366      return null;
367    if ((ex.getValue() instanceof StringType))
368      return ((StringType) ex.getValue()).getValue();
369    if ((ex.getValue() instanceof UriType))
370      return ((UriType) ex.getValue()).getValue();
371    if (ex.getValue() instanceof CodeType)
372      return ((CodeType) ex.getValue()).getValue();
373    if (ex.getValue() instanceof IntegerType)
374      return ((IntegerType) ex.getValue()).asStringValue();
375    if (ex.getValue() instanceof Integer64Type)
376      return ((Integer64Type) ex.getValue()).asStringValue();
377    if (ex.getValue() instanceof DecimalType)
378      return ((DecimalType) ex.getValue()).asStringValue();
379    if ((ex.getValue() instanceof MarkdownType))
380      return ((MarkdownType) ex.getValue()).getValue();
381    return null;
382  }
383
384  @SuppressWarnings("unchecked")
385  public static PrimitiveType<DataType> readPrimitiveExtension(DomainResource c, String uri) {
386    Extension ex = getExtension(c, uri);
387    if (ex == null)
388      return null;
389    return (PrimitiveType<DataType>) ex.getValue();
390  }
391
392  public static boolean findStringExtension(Element c, String uri) {
393    Extension ex = ExtensionHelper.getExtension(c, uri);
394    if (ex == null)
395      return false;
396    if (!(ex.getValue() instanceof StringType))
397      return false;
398    return !StringUtils.isBlank(((StringType) ex.getValue()).getValue());
399  }
400
401  public static Boolean readBooleanExtension(Element c, String uri) {
402    Extension ex = ExtensionHelper.getExtension(c, uri);
403    if (ex == null)
404      return null;
405    if (!(ex.getValue() instanceof BooleanType))
406      return null;
407    return ((BooleanType) ex.getValue()).getValue();
408  }
409
410  public static boolean findBooleanExtension(Element c, String uri) {
411    Extension ex = ExtensionHelper.getExtension(c, uri);
412    if (ex == null)
413      return false;
414    if (!(ex.getValue() instanceof BooleanType))
415      return false;
416    return true;
417  }
418
419  public static Boolean readBooleanExtension(DomainResource c, String uri) {
420    Extension ex = ExtensionHelper.getExtension(c, uri);
421    if (ex == null)
422      return null;
423    if (!(ex.getValue() instanceof BooleanType))
424      return null;
425    return ((BooleanType) ex.getValue()).getValue();
426  }
427
428  public static boolean readBoolExtension(DomainResource c, String uri) {
429    Extension ex = ExtensionHelper.getExtension(c, uri);
430    if (ex == null)
431      return false;
432    if (!(ex.getValue() instanceof BooleanType))
433      return false;
434    return ((BooleanType) ex.getValue()).getValue();
435  }
436
437  public static boolean readBoolExtension(Element e, String uri) {
438    Extension ex = ExtensionHelper.getExtension(e, uri);
439    if (ex == null)
440      return false;
441    if (!(ex.getValue() instanceof BooleanType))
442      return false;
443    return ((BooleanType) ex.getValue()).getValue();
444  }
445
446  public static boolean findBooleanExtension(DomainResource c, String uri) {
447    Extension ex = ExtensionHelper.getExtension(c, uri);
448    if (ex == null)
449      return false;
450    if (!(ex.getValue() instanceof BooleanType))
451      return false;
452    return true;
453  }
454
455  public static String getCSComment(ConceptDefinitionComponent c) {
456    return readStringExtension(c, EXT_CS_COMMENT);    
457  }
458//
459//  public static Boolean getDeprecated(Element c) {
460//    return readBooleanExtension(c, EXT_DEPRECATED);    
461//  }
462
463  public static boolean hasCSComment(ConceptDefinitionComponent c) {
464    return findStringExtension(c, EXT_CS_COMMENT);    
465  }
466
467//  public static boolean hasDeprecated(Element c) {
468//    return findBooleanExtension(c, EXT_DEPRECATED);    
469//  }
470
471  public static void addFlyOver(QuestionnaireItemComponent item, String text, String linkId){
472    if (!StringUtils.isBlank(text)) {
473        QuestionnaireItemComponent display = item.addItem();
474        display.setType(QuestionnaireItemType.DISPLAY);
475        display.setText(text);
476        display.setLinkId(linkId);
477        display.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept("flyover", "http://hl7.org/fhir/questionnaire-item-control", "Fly-over"), true));
478    }
479  }
480
481  public static void addMin(QuestionnaireItemComponent item, int min) {
482    item.getExtension().add(Factory.newExtension(EXT_MINOCCURS, Factory.newInteger(min), true));
483  }
484  
485  public static void addMax(QuestionnaireItemComponent item, int max) {
486    item.getExtension().add(Factory.newExtension(EXT_MAXOCCURS, Factory.newInteger(max), true));
487  }
488  
489  public static void addFhirType(QuestionnaireItemComponent group, String value) {
490    group.getExtension().add(Factory.newExtension(EXT_FHIRTYPE, Factory.newString_(value), true));       
491  }
492
493  public static void addControl(QuestionnaireItemComponent group, String value) {
494    group.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept(value, "http://hl7.org/fhir/questionnaire-item-control", value), true));
495  }
496
497  public static void addAllowedResource(QuestionnaireItemComponent group, String value) {
498    group.getExtension().add(Factory.newExtension(EXT_ALLOWEDRESOURCE, Factory.newCode(value), true));       
499  }
500
501  public static void addReferenceFilter(QuestionnaireItemComponent group, String value) {
502    group.getExtension().add(Factory.newExtension(EXT_REFERENCEFILTER, Factory.newString_(value), true));       
503  }
504
505  public static void addIdentifier(Element element, Identifier value) {
506    element.getExtension().add(Factory.newExtension(EXT_IDENTIFIER, value, true));       
507  }
508
509  /**
510   * @param name the identity of the extension of interest
511   * @return The extension, if on this element, else null
512   */
513  public static Extension getExtension(DomainResource resource, String name) {
514    if (name == null)
515      return null;
516    if (!resource.hasExtension())
517      return null;
518    for (Extension e : resource.getExtension()) {
519      if (name.equals(e.getUrl()))
520        return e;
521    }
522    return null;
523  }
524
525  public static Extension getExtension(Element el, String name) {
526    if (name == null)
527      return null;
528    if (!el.hasExtension())
529      return null;
530    for (Extension e : el.getExtension()) {
531      if (name.equals(e.getUrl()))
532        return e;
533    }
534    return null;
535  }
536
537  public static void setStringExtension(DomainResource resource, String uri, String value) {
538    if (Utilities.noString(value))
539      return;
540        Extension ext = getExtension(resource, uri);
541    if (ext != null)
542      ext.setValue(new StringType(value));
543    else
544      resource.getExtension().add(new Extension(uri).setValue(new StringType(value)));
545  }
546
547  public static void setStringExtension(Element resource, String uri, String value) {
548    if (Utilities.noString(value))
549      return;
550        Extension ext = getExtension(resource, uri);
551    if (ext != null)
552      ext.setValue(new StringType(value));
553    else
554      resource.getExtension().add(new Extension(uri).setValue(new StringType(value)));
555  }
556
557  public static void setCodeExtension(DomainResource resource, String uri, String value) {
558    if (Utilities.noString(value))
559      return;
560    
561    Extension ext = getExtension(resource, uri);
562    if (ext != null)
563      ext.setValue(new CodeType(value));
564    else
565      resource.getExtension().add(new Extension(uri).setValue(new CodeType(value)));
566  }
567
568  public static void setCodeExtension(Element element, String uri, String value) {
569    if (Utilities.noString(value))
570      return;
571    
572    Extension ext = getExtension(element, uri);
573    if (ext != null)
574      ext.setValue(new CodeType(value));
575    else
576      element.getExtension().add(new Extension(uri).setValue(new CodeType(value)));
577  }
578
579  public static void setIntegerExtension(DomainResource resource, String uri, int value) {
580    Extension ext = getExtension(resource, uri);
581    if (ext != null)
582      ext.setValue(new IntegerType(value));
583    else
584      resource.getExtension().add(new Extension(uri).setValue(new IntegerType(value)));
585  }
586
587//  public static String getOID(CodeSystem define) {
588//    return readStringExtension(define, EXT_OID);    
589//  }
590//
591//  public static String getOID(ValueSet vs) {
592//    return readStringExtension(vs, EXT_OID);    
593//  }
594//
595//  public static void setOID(CodeSystem define, String oid) throws FHIRFormatError, URISyntaxException {
596//    if (!oid.startsWith("urn:oid:"))
597//      throw new FHIRFormatError("Error in OID format");
598//    if (oid.startsWith("urn:oid:urn:oid:"))
599//      throw new FHIRFormatError("Error in OID format");
600//    if (!hasExtension(define, EXT_OID))
601//    define.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));       
602//    else if (!oid.equals(readStringExtension(define, EXT_OID)))
603//      throw new Error("Attempt to assign multiple OIDs to a code system");
604//  }
605//  public static void setOID(ValueSet vs, String oid) throws FHIRFormatError, URISyntaxException {
606//    if (!oid.startsWith("urn:oid:"))
607//      throw new FHIRFormatError("Error in OID format");
608//    if (oid.startsWith("urn:oid:urn:oid:"))
609//      throw new FHIRFormatError("Error in OID format");
610//    if (!hasExtension(vs, EXT_OID))
611//    vs.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));       
612//    else if (!oid.equals(readStringExtension(vs, EXT_OID)))
613//      throw new Error("Attempt to assign multiple OIDs to value set "+vs.getName()+" ("+vs.getUrl()+"). Has "+readStringExtension(vs, EXT_OID)+", trying to add "+oid);
614//  }
615
616  public static boolean hasLanguageTranslation(Element element, String lang) {
617    for (Extension e : element.getExtension()) {
618      if (e.getUrl().equals(EXT_TRANSLATION)) {
619        Extension e1 = ExtensionHelper.getExtension(e, "lang");
620
621        if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang))
622          return true;
623      }
624    }
625    return false;
626  }
627
628  public static String getLanguageTranslation(Element element, String lang) {
629    for (Extension e : element.getExtension()) {
630      if (e.getUrl().equals(EXT_TRANSLATION)) {
631        Extension e1 = ExtensionHelper.getExtension(e, "lang");
632
633        if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) {
634          e1 = ExtensionHelper.getExtension(e, "content");
635          return ((StringType) e.getValue()).getValue();
636        }
637      }
638    }
639    return null;
640  }
641
642  public static void addLanguageTranslation(Element element, String lang, String value) {
643    if (Utilities.noString(lang) || Utilities.noString(value))
644      return;
645    
646    Extension extension = new Extension().setUrl(EXT_TRANSLATION);
647    extension.addExtension().setUrl("lang").setValue(new CodeType(lang));
648    extension.addExtension().setUrl("content").setValue(new StringType(value));
649    element.getExtension().add(extension);
650  }
651
652  public static DataType getAllowedUnits(ElementDefinition eld) {
653    for (Extension e : eld.getExtension()) 
654      if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) 
655        return e.getValue();
656    return null;
657  }
658
659  public static void setAllowableUnits(ElementDefinition eld, CodeableConcept cc) {
660    for (Extension e : eld.getExtension()) 
661      if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) {
662        e.setValue(cc);
663        return;
664      }
665    eld.getExtension().add(new Extension().setUrl(EXT_ALLOWABLE_UNITS).setValue(cc));
666  }
667
668  public static List<Extension> getExtensions(Element element, String url) {
669    List<Extension> results = new ArrayList<Extension>();
670    for (Extension ex : element.getExtension())
671      if (ex.getUrl().equals(url))
672        results.add(ex);
673    return results;
674  }
675
676  public static List<Extension> getExtensions(DomainResource resource, String url) {
677    List<Extension> results = new ArrayList<Extension>();
678    for (Extension ex : resource.getExtension())
679      if (ex.getUrl().equals(url))
680        results.add(ex);
681    return results;
682  }
683
684//  public static void addDEReference(DataElement de, String value) {
685//    for (Extension e : de.getExtension()) 
686//      if (e.getUrl().equals(EXT_CIMI_REFERENCE)) {
687//        e.setValue(new UriType(value));
688//        return;
689//      }
690//    de.getExtension().add(new Extension().setUrl(EXT_CIMI_REFERENCE).setValue(new UriType(value)));
691//  }
692
693//  public static void setDeprecated(Element nc) {
694//    for (Extension e : nc.getExtension()) 
695//      if (e.getUrl().equals(EXT_DEPRECATED)) {
696//        e.setValue(new BooleanType(true));
697//        return;
698//      }
699//    nc.getExtension().add(new Extension().setUrl(EXT_DEPRECATED).setValue(new BooleanType(true)));    
700//  }
701
702  public static void setExtension(Element focus, String url, Coding c) {
703    for (Extension e : focus.getExtension()) 
704      if (e.getUrl().equals(url)) {
705        e.setValue(c);
706        return;
707      }
708    focus.getExtension().add(new Extension().setUrl(url).setValue(c));    
709  }
710
711  public static void removeExtension(DomainResource focus, String url) {
712    Iterator<Extension> i = focus.getExtension().iterator();
713    while (i.hasNext()) {
714      Extension e = i.next(); // must be called before you can call i.remove()
715      if (e.getUrl().equals(url)) {
716        i.remove();
717      }
718    }
719  }
720  
721  public static void removeExtension(Element focus, String url) {
722    Iterator<Extension> i = focus.getExtension().iterator();
723    while (i.hasNext()) {
724      Extension e = i.next(); // must be called before you can call i.remove()
725      if (e.getUrl().equals(url)) {
726        i.remove();
727      }
728    }
729  }
730
731  public static int readIntegerExtension(DomainResource dr, String uri, int defaultValue) {
732    Extension ex = ExtensionHelper.getExtension(dr, uri);
733    if (ex == null)
734      return defaultValue;
735    if (ex.getValue() instanceof IntegerType)
736      return ((IntegerType) ex.getValue()).getValue();
737    throw new Error("Unable to read extension "+uri+" as an integer");
738  }
739
740  public static int readIntegerExtension(Element e, String uri, int defaultValue) {
741    Extension ex = ExtensionHelper.getExtension(e, uri);
742    if (ex == null)
743      return defaultValue;
744    if (ex.getValue() instanceof IntegerType)
745      return ((IntegerType) ex.getValue()).getValue();
746    throw new Error("Unable to read extension "+uri+" as an integer");
747  }
748
749  public static Map<String, String> getLanguageTranslations(Element e) {
750    Map<String, String> res = new HashMap<String, String>();
751    for (Extension ext : e.getExtension()) {
752      if (ext.getUrl().equals(EXT_TRANSLATION)) {
753        String lang = readStringExtension(ext, "lang");
754        String value = readStringExtension(ext, "content");
755        res.put(lang,  value);
756      }
757    }
758    return res;
759  }
760
761  public static StandardsStatus getStandardsStatus(DomainResource dr) throws FHIRException {
762    return StandardsStatus.fromCode(ToolingExtensions.readStringExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS));
763  }
764
765  public static void setStandardsStatus(DomainResource dr, StandardsStatus status, String normativeVersion) {
766    if (status == null)
767      ToolingExtensions.removeExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS);
768    else
769      ToolingExtensions.setCodeExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS, status.toCode());
770    if (normativeVersion == null)
771      ToolingExtensions.removeExtension(dr, ToolingExtensions.EXT_NORMATIVE_VERSION);
772    else
773      ToolingExtensions.setCodeExtension(dr, ToolingExtensions.EXT_NORMATIVE_VERSION, normativeVersion);
774  }
775
776  public static void setStandardsStatus(Element dr, StandardsStatus status, String normativeVersion) {
777    if (status == null)
778      ToolingExtensions.removeExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS);
779    else
780      ToolingExtensions.setCodeExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS, status.toCode());
781    if (normativeVersion == null)
782      ToolingExtensions.removeExtension(dr, ToolingExtensions.EXT_NORMATIVE_VERSION);
783    else
784      ToolingExtensions.setCodeExtension(dr, ToolingExtensions.EXT_NORMATIVE_VERSION, normativeVersion);
785  }
786
787  public static ValidationMessage readValidationMessage(OperationOutcomeIssueComponent issue, Source source) {
788    ValidationMessage vm = new ValidationMessage();
789    vm.setSource(source);
790    vm.setLevel(mapSeverity(issue.getSeverity()));
791    vm.setType(mapType(issue.getCode()));
792    if (issue.hasExtension(ToolingExtensions.EXT_ISSUE_LINE))
793      vm.setLine(ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, 0));
794    if (issue.hasExtension(ToolingExtensions.EXT_ISSUE_COL))
795      vm.setCol(ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, 0));
796    if (issue.hasExpression())
797      vm.setLocation(issue.getExpression().get(0).asStringValue());
798    vm.setMessage(issue.getDetails().getText());
799    if (issue.hasExtension("http://hl7.org/fhir/StructureDefinition/rendering-xhtml"))
800      vm.setHtml(ToolingExtensions.readStringExtension(issue, "http://hl7.org/fhir/StructureDefinition/rendering-xhtml"));
801    return vm;
802  }
803
804  private static IssueType mapType(org.hl7.fhir.r4b.model.OperationOutcome.IssueType code) {
805    switch (code) {
806    case BUSINESSRULE: return IssueType.BUSINESSRULE;
807    case CODEINVALID: return IssueType.CODEINVALID;
808    case CONFLICT: return IssueType.CONFLICT;
809    case DELETED: return IssueType.DELETED;
810    case DUPLICATE: return IssueType.DUPLICATE;
811    case EXCEPTION: return IssueType.EXCEPTION;
812    case EXPIRED: return IssueType.EXPIRED;
813    case EXTENSION: return IssueType.EXTENSION;
814    case FORBIDDEN: return IssueType.FORBIDDEN;
815    case INCOMPLETE: return IssueType.INCOMPLETE;
816    case INFORMATIONAL: return IssueType.INFORMATIONAL;
817    case INVALID: return IssueType.INVALID;
818    case INVARIANT: return IssueType.INVARIANT;
819    case LOCKERROR: return IssueType.LOCKERROR;
820    case LOGIN: return IssueType.LOGIN;
821    case MULTIPLEMATCHES: return IssueType.MULTIPLEMATCHES;
822    case NOSTORE: return IssueType.NOSTORE;
823    case NOTFOUND: return IssueType.NOTFOUND;
824    case NOTSUPPORTED: return IssueType.NOTSUPPORTED;
825    case NULL: return IssueType.NULL;
826    case PROCESSING: return IssueType.PROCESSING;
827    case REQUIRED: return IssueType.REQUIRED;
828    case SECURITY: return IssueType.SECURITY;
829    case STRUCTURE: return IssueType.STRUCTURE;
830    case SUPPRESSED: return IssueType.SUPPRESSED;
831    case THROTTLED: return IssueType.THROTTLED;
832    case TIMEOUT: return IssueType.TIMEOUT;
833    case TOOCOSTLY: return IssueType.TOOCOSTLY;
834    case TOOLONG: return IssueType.TOOLONG;
835    case TRANSIENT: return IssueType.TRANSIENT;
836    case UNKNOWN: return IssueType.UNKNOWN;
837    case VALUE: return IssueType.VALUE;
838    default: return null;
839    }
840  }
841
842  private static IssueSeverity mapSeverity(org.hl7.fhir.r4b.model.OperationOutcome.IssueSeverity severity) {
843    switch (severity) {
844    case ERROR: return IssueSeverity.ERROR;
845    case FATAL: return IssueSeverity.FATAL;
846    case INFORMATION: return IssueSeverity.INFORMATION;
847    case WARNING: return IssueSeverity.WARNING;
848    default: return null;
849    }
850  }
851
852  public static String getPresentation(PrimitiveType<?> type) {
853    if (type.hasExtension(EXT_RENDERED_VALUE))
854      return readStringExtension(type, EXT_RENDERED_VALUE);
855    return type.primitiveValue();
856  }
857  
858  public static String getPresentation(Element holder, PrimitiveType<?> type) {
859    if (holder.hasExtension(EXT_RENDERED_VALUE))
860      return readStringExtension(holder, EXT_RENDERED_VALUE);
861    if (type.hasExtension(EXT_RENDERED_VALUE))
862      return readStringExtension(type, EXT_RENDERED_VALUE);
863    return type.primitiveValue();
864  }
865  
866//  public static boolean hasOID(ValueSet vs) {
867//    return hasExtension(vs, EXT_OID);
868//  }
869//  
870//  public static boolean hasOID(CodeSystem cs) {
871//    return hasExtension(cs, EXT_OID);
872//  }
873//  
874  public static void addUrlExtension(Element e, String url, String content) {
875    if (!StringUtils.isBlank(content)) {
876      Extension ex = getExtension(e, url);
877      if (ex != null)
878        ex.setValue(new UrlType(content));
879      else
880        e.getExtension().add(Factory.newExtension(url, new UrlType(content), true));   
881    }
882  }
883
884  public static void addUrlExtension(DomainResource dr, String url, String value) {
885    Extension ex = getExtension(dr, url);
886    if (ex != null)
887      ex.setValue(new UrlType(value));
888    else
889      dr.getExtension().add(Factory.newExtension(url, new UrlType(value), true));   
890  }
891
892  public static void addUriExtension(Element e, String url, String content) {
893    if (!StringUtils.isBlank(content)) {
894      Extension ex = getExtension(e, url);
895      if (ex != null)
896        ex.setValue(new UriType(content));
897      else
898        e.getExtension().add(Factory.newExtension(url, new UriType(content), true));   
899    }
900  }
901
902  public static void addUriExtension(DomainResource dr, String url, String value) {
903    Extension ex = getExtension(dr, url);
904    if (ex != null)
905      ex.setValue(new UriType(value));
906    else
907      dr.getExtension().add(Factory.newExtension(url, new UriType(value), true));   
908  }
909
910  public static boolean usesExtension(String url, Base base) {
911    if ("Extension".equals(base.fhirType())) {
912      Property p = base.getNamedProperty("url");
913      for (Base b : p.getValues()) {
914        if (url.equals(b.primitiveValue())) {
915          return true;
916        }
917      }
918    }
919    
920    for (Property p : base.children() ) {
921      for (Base v : p.getValues()) {
922        if (usesExtension(url, v)) {
923          return true;
924        }
925      }
926    }
927    return false;
928  }
929
930  
931}