001package org.hl7.fhir.r5.terminologies;
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
033import org.hl7.fhir.exceptions.FHIRException;
034import org.hl7.fhir.r5.context.IWorkerContext;
035import org.hl7.fhir.r5.model.CanonicalType;
036import org.hl7.fhir.r5.model.CodeSystem;
037import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
038import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
039import org.hl7.fhir.r5.model.Identifier;
040import org.hl7.fhir.r5.model.Meta;
041import org.hl7.fhir.r5.model.UriType;
042import org.hl7.fhir.r5.model.ValueSet;
043import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
044import org.hl7.fhir.r5.utils.ToolingExtensions;
045import org.hl7.fhir.utilities.StandardsStatus;
046import org.hl7.fhir.utilities.Utilities;
047
048public class ValueSetUtilities {
049
050  public static ValueSet makeShareable(ValueSet vs) {
051    if (!vs.hasExperimental()) {
052      vs.setExperimental(false);
053    }
054    if (!vs.hasMeta())
055      vs.setMeta(new Meta());
056    for (UriType t : vs.getMeta().getProfile()) 
057      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset"))
058        return vs;
059    vs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablevalueset"));
060    return vs;
061  }
062
063  public static boolean makeVSShareable(ValueSet vs) {
064    if (!vs.hasMeta())
065      vs.setMeta(new Meta());
066    for (UriType t : vs.getMeta().getProfile()) 
067      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset"))
068        return false;
069    vs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablevalueset"));
070    return true;
071  }
072
073  public static void checkShareable(ValueSet vs) {
074    if (!vs.hasMeta())
075      throw new Error("ValueSet "+vs.getUrl()+" is not shareable");
076    for (UriType t : vs.getMeta().getProfile()) {
077      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset"))
078        return;
079    }
080    throw new Error("ValueSet "+vs.getUrl()+" is not shareable");    
081  }
082
083  public static boolean hasOID(ValueSet vs) {
084    return getOID(vs) != null;
085  }
086
087  public static String getOID(ValueSet vs) {
088    for (Identifier id : vs.getIdentifier()) {
089      if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:"))
090        return id.getValue().substring(8);
091    }
092    return null;
093  }
094
095  public static void setOID(ValueSet vs, String oid) {
096    if (!oid.startsWith("urn:oid:"))
097      oid = "urn:oid:" + oid;
098    for (Identifier id : vs.getIdentifier()) {
099      if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:")) {
100        id.setValue(oid);
101        return;
102      }
103    }
104    vs.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(oid);
105  }
106
107  public static void markStatus(ValueSet vs, String wg, StandardsStatus status, String pckage, String fmm, IWorkerContext context, String normativeVersion) throws FHIRException {
108    if (vs.hasUserData("external.url"))
109      return;
110    
111    if (wg != null) {
112      if (!ToolingExtensions.hasExtension(vs, ToolingExtensions.EXT_WORKGROUP) || 
113          (!Utilities.existsInList(ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_WORKGROUP), "fhir", "vocab") && Utilities.existsInList(wg, "fhir", "vocab"))) {
114        ToolingExtensions.setCodeExtension(vs, ToolingExtensions.EXT_WORKGROUP, wg);
115      }
116    }
117    if (status != null) {
118      StandardsStatus ss = ToolingExtensions.getStandardsStatus(vs);
119      if (ss == null || ss.isLowerThan(status)) 
120        ToolingExtensions.setStandardsStatus(vs, status, normativeVersion);
121      if (pckage != null) {
122        if (!vs.hasUserData("ballot.package"))        
123          vs.setUserData("ballot.package", pckage);
124        else if (!pckage.equals(vs.getUserString("ballot.package")))
125          if (!"infrastructure".equals(vs.getUserString("ballot.package")))
126          System.out.println("Value Set "+vs.getUrl()+": ownership clash "+pckage+" vs "+vs.getUserString("ballot.package"));
127      }
128      if (status == StandardsStatus.NORMATIVE) {
129        vs.setExperimental(false);
130        vs.setStatus(PublicationStatus.ACTIVE);
131      }
132    }
133    if (fmm != null) {
134      String sfmm = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_FMM_LEVEL);
135      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm))  {
136        ToolingExtensions.setIntegerExtension(vs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
137      }
138      if (Integer.parseInt(fmm) <= 1) {
139        vs.setExperimental(true);
140      }
141    }
142    if (vs.hasUserData("cs"))
143      CodeSystemUtilities.markStatus((CodeSystem) vs.getUserData("cs"), wg, status, pckage, fmm, normativeVersion);
144    else if (status == StandardsStatus.NORMATIVE && context != null) {
145      for (ConceptSetComponent csc : vs.getCompose().getInclude()) {
146        if (csc.hasSystem()) {
147          CodeSystem cs = context.fetchCodeSystem(csc.getSystem());
148          if (cs != null) {
149            CodeSystemUtilities.markStatus(cs, wg, status, pckage, fmm, normativeVersion);
150          }
151        }
152      }
153    }
154  }
155
156  private static int ssval(String status) {
157    if ("Draft".equals("status")) 
158      return 1;
159    if ("Informative".equals("status")) 
160      return 2;
161    if ("External".equals("status")) 
162      return 3;
163    if ("Trial Use".equals("status")) 
164      return 3;
165    if ("Normative".equals("status")) 
166      return 4;
167    return -1;
168  }
169
170  public static ValueSet generateImplicitValueSet(String uri) {
171    if (uri.startsWith("http://snomed.info/sct"))
172      return generateImplicitSnomedValueSet(uri);
173    if (uri.startsWith("http://loinc.org/vs"))
174      return generateImplicitLoincValueSet(uri);
175    if (uri.equals("http://hl7.org/fhir/ValueSet/mimetypes")) {
176      return generateImplicitMimetypesValueSet(uri);
177    }
178    return null;
179  }
180
181  private static ValueSet generateImplicitMimetypesValueSet(String theUri) {
182    ValueSet valueSet = new ValueSet();
183    valueSet.setStatus(PublicationStatus.ACTIVE);
184    valueSet.setUrl(theUri);
185    valueSet.setDescription("This value set includes all possible codes from BCP-13 (http://tools.ietf.org/html/bcp13)");
186    valueSet.getCompose()
187      .addInclude().setSystem("urn:ietf:bcp:13");
188    return valueSet;
189  }
190
191  private static ValueSet generateImplicitLoincValueSet(String uri) {
192    if ("http://loinc.org/vs".equals(uri))
193      return makeLoincValueSet();
194    if (uri.startsWith("http://loinc.org/vs/LL"))
195      return makeAnswerList(makeLoincValueSet(), uri);
196    return null;
197  }
198
199  private static ValueSet makeAnswerList(ValueSet vs, String uri) {
200    vs.setUrl(uri);
201    String c = uri.substring(20);
202    vs.setName("LOINCAnswers"+c);
203    vs.setTitle("LOINC Answer Codes for "+c);
204    vs.getCompose().getIncludeFirstRep().addFilter().setProperty("LIST").setOp(FilterOperator.EQUAL).setValue(c);
205    return vs;
206  }
207
208  private static ValueSet makeLoincValueSet() {
209    ValueSet vs = new ValueSet();
210    vs.setUrl("http://loinc.org/vs");
211    vs.setName("LOINCCodes");
212    vs.setTitle("All LOINC codes");
213    vs.setCopyright("This content LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use");
214    vs.setStatus(PublicationStatus.ACTIVE);
215    vs.getCompose().addInclude().setSystem("http://loinc.org");
216    return vs;
217  }
218
219  private static ValueSet generateImplicitSnomedValueSet(String uri) {
220    if ("http://snomed.info/sct?fhir_vs".equals(uri))
221      return makeImplicitSnomedValueSet(uri);
222    return null;
223  }
224
225  private static ValueSet makeImplicitSnomedValueSet(String uri) {
226    ValueSet vs = new ValueSet();
227    vs.setUrl(uri);
228    vs.setName("SCTValueSet");
229    vs.setTitle("SCT ValueSet");
230    vs.setDescription("All SNOMED CT Concepts");
231    vs.setCopyright("This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (SNOMED International), and distributed by agreement between SNOMED International and HL7. Implementer use of SNOMED CT is not covered by this agreement");
232    vs.setStatus(PublicationStatus.ACTIVE);
233    vs.getCompose().addInclude().setSystem("http://snomed.info/sct");
234    return vs;
235  }
236
237}