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