001package org.hl7.fhir.r4.terminologies;
002
003import java.util.Calendar;
004import java.util.List;
005
006import org.hl7.fhir.r4.model.BooleanType;
007import org.hl7.fhir.r4.model.CanonicalType;
008import org.hl7.fhir.r4.model.CodeSystem;
009import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
010import org.hl7.fhir.r4.model.CodeSystem.ConceptPropertyComponent;
011import org.hl7.fhir.r4.model.CodeSystem.PropertyComponent;
012import org.hl7.fhir.r4.model.CodeSystem.PropertyType;
013import org.hl7.fhir.r4.utils.ToolingExtensions;
014import org.hl7.fhir.r4.model.DateTimeType;
015import org.hl7.fhir.r4.model.Identifier;
016import org.hl7.fhir.r4.model.Meta;
017import org.hl7.fhir.r4.model.Type;
018import org.hl7.fhir.r4.model.UriType;
019import org.hl7.fhir.exceptions.FHIRException;
020import org.hl7.fhir.exceptions.FHIRFormatError;
021import org.hl7.fhir.utilities.StandardsStatus;
022import org.hl7.fhir.utilities.Utilities;
023
024public class CodeSystemUtilities {
025
026  public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def) {
027    for (ConceptPropertyComponent p : def.getProperty()) {
028      if (p.getCode().equals("deprecated") && p.hasValue() && p.getValue() instanceof BooleanType) 
029        return ((BooleanType) p.getValue()).getValue();
030      if (p.getCode().equals("deprecationDate") && p.hasValue() && p.getValue() instanceof DateTimeType) 
031        return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance()));
032    }
033    return false;
034  }
035
036  public static boolean isNotSelectable(CodeSystem cs, ConceptDefinitionComponent def) {
037    for (ConceptPropertyComponent p : def.getProperty()) {
038      if (p.getCode().equals("notSelectable") && p.hasValue() && p.getValue() instanceof BooleanType) 
039        return ((BooleanType) p.getValue()).getValue();
040    }
041    return false;
042  }
043
044  public static void setNotSelectable(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError {
045    defineNotSelectableProperty(cs);
046    concept.addProperty().setCode("notSelectable").setValue(new BooleanType(true));    
047  }
048
049  public static void setInactive(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError {
050    defineInactiveProperty(cs);
051    concept.addProperty().setCode("inactive").setValue(new BooleanType(true));    
052  }
053
054  public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept, DateTimeType date) throws FHIRFormatError {
055    defineDeprecatedProperty(cs);
056    concept.addProperty().setCode("deprecationDate").setValue(date);    
057  }
058
059  public static void defineNotSelectableProperty(CodeSystem cs) {
060    defineCodeSystemProperty(cs, "notSelectable", "Indicates that the code is abstract - only intended to be used as a selector for other concepts", PropertyType.BOOLEAN);
061  }
062
063  public static void defineInactiveProperty(CodeSystem cs) {
064    defineCodeSystemProperty(cs, "inactive", "True if the concept is not considered active - e.g. not a valid concept any more", PropertyType.BOOLEAN);
065  }
066
067  public static void defineDeprecatedProperty(CodeSystem cs) {
068    defineCodeSystemProperty(cs, "deprecationDate", "The date at which a concept was deprecated. Concepts that are deprecated but not inactive can still be used, but their use is discouraged", PropertyType.DATETIME);
069  }
070
071  public static void defineCodeSystemProperty(CodeSystem cs, String code, String description, PropertyType type) {
072    for (PropertyComponent p : cs.getProperty()) {
073      if (p.getCode().equals(code))
074        return;
075    }
076    cs.addProperty().setCode(code).setDescription(description).setType(type).setUri("http://hl7.org/fhir/concept-properties#"+code);
077  }
078
079  public static String getCodeDefinition(CodeSystem cs, String code) {
080    return getCodeDefinition(cs.getConcept(), code);
081  }
082
083  private static String getCodeDefinition(List<ConceptDefinitionComponent> list, String code) {
084    for (ConceptDefinitionComponent c : list) {
085      if (c.hasCode() &&  c.getCode().equals(code))
086        return c.getDefinition();
087      String s = getCodeDefinition(c.getConcept(), code);
088      if (s != null)
089        return s;
090    }
091    return null;
092  }
093
094  public static CodeSystem makeShareable(CodeSystem cs) {
095    if (!cs.hasMeta())
096      cs.setMeta(new Meta());
097    for (UriType t : cs.getMeta().getProfile()) 
098      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"))
099        return cs;
100    cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"));
101    return cs;
102  }
103
104  public static void setOID(CodeSystem cs, String oid) {
105    if (!oid.startsWith("urn:oid:"))
106       oid = "urn:oid:" + oid;
107    if (!cs.hasIdentifier())
108      cs.setIdentifier(new Identifier().setSystem("urn:ietf:rfc:3986").setValue(oid));
109    else if ("urn:ietf:rfc:3986".equals(cs.getIdentifier().getSystem()) && cs.getIdentifier().hasValue() && cs.getIdentifier().getValue().startsWith("urn:oid:"))
110      cs.getIdentifier().setValue(oid);
111    else
112      throw new Error("unable to set OID on code system");
113    
114  }
115
116  public static boolean hasOID(CodeSystem cs) {
117    return getOID(cs) != null;
118  }
119
120  public static String getOID(CodeSystem cs) {
121    if (cs.hasIdentifier() && "urn:ietf:rfc:3986".equals(cs.getIdentifier().getSystem()) && cs.getIdentifier().hasValue() && cs.getIdentifier().getValue().startsWith("urn:oid:"))
122        return cs.getIdentifier().getValue().substring(8);
123    return null;
124  }
125
126  public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException {
127    for (ConceptPropertyComponent p : def.getProperty()) {
128      if (p.getCode().equals("status") && p.hasValueStringType()) 
129        return "inactive".equals(p.getValueStringType());
130    }
131    return false;
132  }
133  
134  public static boolean isInactive(CodeSystem cs, String code) throws FHIRException {
135    ConceptDefinitionComponent def = findCode(cs.getConcept(), code);
136    if (def == null)
137      return true;
138    return isInactive(cs, def);
139  }
140
141  private static ConceptDefinitionComponent findCode(List<ConceptDefinitionComponent> list, String code) {
142    for (ConceptDefinitionComponent c : list) {
143      if (c.getCode().equals(code))
144        return c;
145      ConceptDefinitionComponent s = findCode(c.getConcept(), code);
146      if (s != null)
147        return s;
148    }
149    return null;
150  }
151
152  public static void markStatus(CodeSystem cs, String wg, StandardsStatus status, String pckage, String fmm) throws FHIRException {
153    if (wg != null) {
154      if (!ToolingExtensions.hasExtension(cs, ToolingExtensions.EXT_WORKGROUP) || 
155          (Utilities.existsInList(ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_WORKGROUP), "fhir", "vocab") && !Utilities.existsInList(wg, "fhir", "vocab"))) {
156        ToolingExtensions.setCodeExtension(cs, ToolingExtensions.EXT_WORKGROUP, wg);
157      }
158    }
159    if (status != null) {
160      StandardsStatus ss = StandardsStatus.fromCode(ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_BALLOT_STATUS));
161      if (ss == null || ss.isLowerThan(status)) 
162        ToolingExtensions.setStringExtension(cs, ToolingExtensions.EXT_BALLOT_STATUS, status.toDisplay());
163      if (pckage != null) {
164        if (!cs.hasUserData("ballot.package"))
165          cs.setUserData("ballot.package", pckage);
166        else if (!pckage.equals(cs.getUserString("ballot.package")))
167          System.out.println("Code System "+cs.getUrl()+": ownership clash "+pckage+" vs "+cs.getUserString("ballot.package"));
168      }
169    }
170    if (fmm != null) {
171      String sfmm = ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_FMM_LEVEL);
172      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) 
173        ToolingExtensions.setIntegerExtension(cs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
174    }
175  }
176
177 
178  public static Type readProperty(ConceptDefinitionComponent concept, String code) {
179    for (ConceptPropertyComponent p : concept.getProperty())
180      if (p.getCode().equals(code))
181        return p.getValue(); 
182    return null;
183  }
184
185}