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