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}