001package org.hl7.fhir.dstu2016may.hapi.validation; 002 003import ca.uhn.fhir.i18n.Msg; 004import ca.uhn.fhir.context.FhirContext; 005import ca.uhn.fhir.context.support.ConceptValidationOptions; 006import ca.uhn.fhir.context.support.IValidationSupport; 007import ca.uhn.fhir.context.support.ValidationSupportContext; 008import ca.uhn.fhir.rest.api.Constants; 009import org.apache.commons.lang3.Validate; 010import org.hl7.fhir.dstu2016may.formats.IParser; 011import org.hl7.fhir.dstu2016may.formats.ParserType; 012import org.hl7.fhir.dstu2016may.model.CodeSystem; 013import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent; 014import org.hl7.fhir.dstu2016may.model.CodeType; 015import org.hl7.fhir.dstu2016may.model.CodeableConcept; 016import org.hl7.fhir.dstu2016may.model.Coding; 017import org.hl7.fhir.dstu2016may.model.ConceptMap; 018import org.hl7.fhir.dstu2016may.model.OperationOutcome; 019import org.hl7.fhir.dstu2016may.model.Resource; 020import org.hl7.fhir.dstu2016may.model.ResourceType; 021import org.hl7.fhir.dstu2016may.model.StructureDefinition; 022import org.hl7.fhir.dstu2016may.model.ValueSet; 023import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent; 024import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent; 025import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent; 026import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent; 027import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome; 028import org.hl7.fhir.dstu2016may.utils.INarrativeGenerator; 029import org.hl7.fhir.dstu2016may.utils.IWorkerContext; 030import org.hl7.fhir.utilities.i18n.I18nBase; 031 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.HashSet; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040 041import static org.apache.commons.lang3.StringUtils.isBlank; 042import static org.apache.commons.lang3.StringUtils.isNotBlank; 043 044public final class HapiWorkerContext extends I18nBase implements IWorkerContext { 045 private final FhirContext myCtx; 046 private Map<String, Resource> myFetchedResourceCache = new HashMap<>(); 047 private IValidationSupport myValidationSupport; 048 049 public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { 050 Validate.notNull(theCtx, "theCtx must not be null"); 051 Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); 052 myCtx = theCtx; 053 myValidationSupport = theValidationSupport; 054 055 setValidationMessageLanguage(getLocale()); 056 } 057 058 @Override 059 public List<StructureDefinition> allStructures() { 060 return myValidationSupport.fetchAllStructureDefinitions(); 061 } 062 063 @Override 064 public CodeSystem fetchCodeSystem(String theSystem) { 065 if (myValidationSupport == null) { 066 return null; 067 } else { 068 return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem); 069 } 070 } 071 072 @Override 073 public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) { 074 if (myValidationSupport == null) { 075 return null; 076 } else { 077 @SuppressWarnings("unchecked") 078 T retVal = (T) myFetchedResourceCache.get(theUri); 079 if (retVal == null) { 080 retVal = myValidationSupport.fetchResource(theClass, theUri); 081 if (retVal != null) { 082 myFetchedResourceCache.put(theUri, retVal); 083 } 084 } 085 return retVal; 086 } 087 } 088 089 @Override 090 public List<ConceptMap> findMapsForSource(String theUrl) { 091 throw new UnsupportedOperationException(Msg.code(470)); 092 } 093 094 @Override 095 public String getAbbreviation(String theName) { 096 throw new UnsupportedOperationException(Msg.code(471)); 097 } 098 099 100 @Override 101 public IParser getParser(ParserType theType) { 102 throw new UnsupportedOperationException(Msg.code(472)); 103 } 104 105 @Override 106 public IParser getParser(String theType) { 107 throw new UnsupportedOperationException(Msg.code(473)); 108 } 109 110 @Override 111 public List<String> getResourceNames() { 112 List<String> result = new ArrayList<>(); 113 for (ResourceType next : ResourceType.values()) { 114 result.add(next.name()); 115 } 116 Collections.sort(result); 117 return result; 118 } 119 120 @Override 121 public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) { 122 throw new UnsupportedOperationException(Msg.code(474)); 123 } 124 125 @Override 126 public IParser newJsonParser() { 127 throw new UnsupportedOperationException(Msg.code(475)); 128 } 129 130 @Override 131 public IParser newXmlParser() { 132 throw new UnsupportedOperationException(Msg.code(476)); 133 } 134 135 @Override 136 public INarrativeGenerator getNarrativeGenerator(String theS, String theS1) { 137 throw new UnsupportedOperationException(Msg.code(477)); 138 } 139 140 @Override 141 public String oid2Uri(String theCode) { 142 throw new UnsupportedOperationException(Msg.code(478)); 143 } 144 145 @Override 146 public StructureDefinition fetchTypeDefinition(String typeName) { 147 return fetchResource(org.hl7.fhir.dstu2016may.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); 148 } 149 150 @Override 151 public boolean supportsSystem(String theSystem) { 152 if (myValidationSupport == null) { 153 return false; 154 } else { 155 return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem); 156 } 157 } 158 159 @Override 160 public Set<String> typeTails() { 161 return new HashSet<>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code", 162 "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint", 163 "Timing", "Reference", "Annotation", "Signature", "Meta")); 164 } 165 166 @Override 167 public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) { 168 for (Coding next : theCode.getCoding()) { 169 ValidationResult retVal = validateCode(next, theVs); 170 if (retVal.isOk()) { 171 return retVal; 172 } 173 } 174 175 return new ValidationResult(null, null); 176 } 177 178 @Override 179 public ValidationResult validateCode(Coding theCode, ValueSet theVs) { 180 String system = theCode.getSystem(); 181 String code = theCode.getCode(); 182 String display = theCode.getDisplay(); 183 return validateCode(system, code, display, theVs); 184 } 185 186 @Override 187 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) { 188 IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), theSystem, theCode, theDisplay, null); 189 if (result == null) { 190 return null; 191 } 192 OperationOutcome.IssueSeverity severity = null; 193 if (result.getSeverity() != null) { 194 severity = OperationOutcome.IssueSeverity.fromCode(result.getSeverityCode()); 195 } 196 ConceptDefinitionComponent definition = result.getCode() != null ? new ConceptDefinitionComponent().setCode(result.getCode()) : null; 197 return new ValidationResult(severity, result.getMessage(), definition); 198 } 199 200 @Override 201 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) { 202 throw new UnsupportedOperationException(Msg.code(479)); 203 } 204 205 @Override 206 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) { 207 208 if (theVs != null && isNotBlank(theCode)) { 209 for (ConceptSetComponent next : theVs.getCompose().getInclude()) { 210 if (isBlank(theSystem) || theSystem.equals(next.getSystem())) { 211 for (ConceptReferenceComponent nextCode : next.getConcept()) { 212 if (theCode.equals(nextCode.getCode())) { 213 CodeType code = new CodeType(theCode); 214 return new ValidationResult(new ConceptDefinitionComponent(code)); 215 } 216 } 217 } 218 } 219 } 220 221 222 boolean caseSensitive = true; 223 if (isNotBlank(theSystem)) { 224 CodeSystem system = fetchCodeSystem(theSystem); 225 if (system == null) { 226 return new ValidationResult(OperationOutcome.IssueSeverity.INFORMATION, "Code " + Constants.codeSystemWithDefaultDescription(theSystem) + "/" + theCode + " was not validated because the code system is not present"); 227 } 228 229 if (system.hasCaseSensitive()) { 230 caseSensitive = system.getCaseSensitive(); 231 } 232 } 233 234 String wantCode = theCode; 235 if (!caseSensitive) { 236 wantCode = wantCode.toUpperCase(); 237 } 238 239 ValueSetExpansionOutcome expandedValueSet = null; 240 241 /* 242 * The following valueset is a special case, since the BCP codesystem is very difficult to expand 243 */ 244 if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) { 245 ValueSet expansion = new ValueSet(); 246 for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) { 247 for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) { 248 expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay()); 249 } 250 } 251 expandedValueSet = new ValueSetExpansionOutcome(expansion); 252 } 253 254 if (expandedValueSet == null) { 255 expandedValueSet = expandVS(theVs, true); 256 } 257 258 for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) { 259 String nextCode = next.getCode(); 260 if (!caseSensitive) { 261 nextCode = nextCode.toUpperCase(); 262 } 263 264 if (nextCode.equals(wantCode)) { 265 if (theSystem == null || next.getSystem().equals(theSystem)) { 266 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 267 definition.setCode(next.getCode()); 268 definition.setDisplay(next.getDisplay()); 269 return new ValidationResult(definition); 270 } 271 } 272 } 273 274 return new ValidationResult(OperationOutcome.IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem) + "]"); 275 } 276 277 @Override 278 public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk) { 279 throw new UnsupportedOperationException(Msg.code(480)); 280 } 281 282 @Override 283 public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) { 284 throw new UnsupportedOperationException(Msg.code(481)); 285 } 286 287 288}