Merge validator

This commit is contained in:
James Agnew 2018-05-09 17:22:34 -04:00
parent a0c40cf98a
commit adc73e75c9
32 changed files with 4934 additions and 3720 deletions

View File

@ -123,6 +123,19 @@ public class BundleUtil {
return null;
}
public static Integer getTotal(FhirContext theContext, IBaseBundle theBundle) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
BaseRuntimeChildDefinition entryChild = def.getChildByName("total");
List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
if (entries.size() > 0) {
IPrimitiveType<Number> typeElement = (IPrimitiveType<Number>) entries.get(0);
if (typeElement != null && typeElement.getValue() != null) {
return typeElement.getValue().intValue();
}
}
return null;
}
/**
* Extract all of the resources from a given bundle
*/

View File

@ -33,6 +33,11 @@
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-igpacks</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.cli;
*/
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
import org.hl7.fhir.instance.model.StructureDefinition;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
@ -31,12 +32,20 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import java.util.ArrayList;
import java.util.List;
public class LoadingValidationSupportDstu2 implements IValidationSupport {
private FhirContext myCtx = FhirContext.forDstu2Hl7Org();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class);
@Override
public List<StructureDefinition> allStructures() {
return new ArrayList<>();
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;

View File

@ -21,21 +21,29 @@ package ca.uhn.fhir.cli;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu2;
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu3;
import ca.uhn.fhir.parser.LenientErrorHandler;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import com.google.common.base.Charsets;
import com.phloc.commons.io.file.FileUtils;
import org.apache.commons.cli.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.fusesource.jansi.Ansi.Color;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import static org.apache.commons.lang3.StringUtils.*;
import static org.fusesource.jansi.Ansi.ansi;
@ -68,13 +76,27 @@ public class ValidateCommand extends BaseCommand {
retVal.addOption("s", "sch", false, "Validate using Schematrons");
retVal.addOption("p", "profile", false, "Validate using Profiles (StructureDefinition / ValueSet)");
retVal.addOption("r", "fetch-remote", false,
"Allow fetching remote resources (in other words, if a resource being validated refers to an external StructureDefinition, Questionnaire, etc. this flag allows the validator to access the internet to try and fetch this resource)");
"Allow fetching remote resources (in other words, if a resource being validated refers to an external StructureDefinition, Questionnaire, etc. this flag allows the validator to access the internet to try and fetch this resource)");
retVal.addOption(new Option("l", "fetch-local", true, "Fetch a profile locally and use it if referenced"));
retVal.addOption("e", "encoding", false, "File encoding (default is UTF-8)");
addOptionalOption(retVal, null, "igpack", true, "If specified, provides the filename of an IGPack file to include in validation");
return retVal;
}
private String loadFile(String theFileName) throws ParseException {
return new String(loadFileAsByteArray(theFileName), Charsets.UTF_8);
}
private byte[] loadFileAsByteArray(String theFileName) throws ParseException {
byte[] input;
try {
input = IOUtils.toByteArray(new FileInputStream(new File(theFileName)));
} catch (IOException e) {
throw new ParseException("Failed to load file '" + theFileName + "' - Error: " + e.toString());
}
return input;
}
@Override
public void run(CommandLine theCommandLine) throws ParseException {
parseFhirContext(theCommandLine);
@ -112,48 +134,64 @@ public class ValidateCommand extends BaseCommand {
if (theCommandLine.hasOption("l")) {
String localProfile = theCommandLine.getOptionValue("l");
ourLog.info("Loading profile: {}", localProfile);
String input;
try {
input = IOUtils.toString(new FileReader(new File(localProfile)));
} catch (IOException e) {
throw new ParseException("Failed to load file '" + localProfile + "' - Error: " + e.toString());
}
String input = loadFile(localProfile);
localProfileResource = ca.uhn.fhir.rest.api.EncodingEnum.detectEncodingNoDefault(input).newParser(ctx).parseResource(input);
}
byte[] igPack = null;
String igpackFilename = null;
if (theCommandLine.hasOption("igpack")) {
igpackFilename = theCommandLine.getOptionValue("igpack");
igPack = loadFileAsByteArray(igpackFilename);
}
if (theCommandLine.hasOption("p")) {
switch (ctx.getVersion().getVersion()) {
case DSTU2: {
org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator instanceValidator = new org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator();
val.registerValidatorModule(instanceValidator);
org.hl7.fhir.instance.hapi.validation.ValidationSupportChain validationSupport = new org.hl7.fhir.instance.hapi.validation.ValidationSupportChain(
case DSTU2: {
org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator instanceValidator = new org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator();
val.registerValidatorModule(instanceValidator);
org.hl7.fhir.instance.hapi.validation.ValidationSupportChain validationSupport = new org.hl7.fhir.instance.hapi.validation.ValidationSupportChain(
new org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport());
if (localProfileResource != null) {
org.hl7.fhir.instance.model.StructureDefinition convertedSd = FhirContext.forDstu2Hl7Org().newXmlParser().parseResource(org.hl7.fhir.instance.model.StructureDefinition.class, ctx.newXmlParser().encodeResourceToString(localProfileResource));
instanceValidator.setStructureDefintion(convertedSd);
if (igPack != null) {
FhirContext hl7orgCtx = FhirContext.forDstu2Hl7Org();
hl7orgCtx.setParserErrorHandler(new LenientErrorHandler(false));
IgPackParserDstu2 parser = new IgPackParserDstu2(hl7orgCtx);
org.hl7.fhir.instance.hapi.validation.IValidationSupport igValidationSupport = parser.parseIg(igPack, igpackFilename);
validationSupport.addValidationSupport(igValidationSupport);
}
if (localProfileResource != null) {
org.hl7.fhir.instance.model.StructureDefinition convertedSd = FhirContext.forDstu2Hl7Org().newXmlParser().parseResource(org.hl7.fhir.instance.model.StructureDefinition.class, ctx.newXmlParser().encodeResourceToString(localProfileResource));
instanceValidator.setStructureDefintion(convertedSd);
}
if (theCommandLine.hasOption("r")) {
validationSupport.addValidationSupport(new LoadingValidationSupportDstu2());
}
instanceValidator.setValidationSupport(validationSupport);
break;
}
if (theCommandLine.hasOption("r")) {
validationSupport.addValidationSupport(new LoadingValidationSupportDstu2());
case DSTU3: {
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
val.registerValidatorModule(instanceValidator);
ValidationSupportChain validationSupport = new ValidationSupportChain(new DefaultProfileValidationSupport());
if (igPack != null) {
IgPackParserDstu3 parser = new IgPackParserDstu3(getFhirContext());
IValidationSupport igValidationSupport = parser.parseIg(igPack, igpackFilename);
validationSupport.addValidationSupport(igValidationSupport);
}
if (localProfileResource != null) {
instanceValidator.setStructureDefintion((StructureDefinition) localProfileResource);
}
if (theCommandLine.hasOption("r")) {
validationSupport.addValidationSupport(new LoadingValidationSupportDstu3());
}
instanceValidator.setValidationSupport(validationSupport);
break;
}
instanceValidator.setValidationSupport(validationSupport);
break;
}
case DSTU3: {
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
val.registerValidatorModule(instanceValidator);
ValidationSupportChain validationSupport = new ValidationSupportChain(new DefaultProfileValidationSupport());
if (localProfileResource != null) {
instanceValidator.setStructureDefintion((StructureDefinition) localProfileResource);
}
if (theCommandLine.hasOption("r")) {
validationSupport.addValidationSupport(new LoadingValidationSupportDstu3());
}
instanceValidator.setValidationSupport(validationSupport);
break;
}
default:
throw new ParseException("Profile validation (-p) is not supported for this FHIR version");
default:
throw new ParseException("Profile validation (-p) is not supported for this FHIR version");
}
}
@ -193,7 +231,7 @@ public class ValidateCommand extends BaseCommand {
if (results.isSuccessful()) {
ourLog.info("Validation successful!");
} else {
ourLog.warn("Validation FAILED");
throw new CommandFailureException("Validation failed");
}
}
}

View File

@ -3,6 +3,8 @@ package ca.uhn.fhir.cli;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.fail;
public class ValidateTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateTest.class);
@ -16,8 +18,43 @@ public class ValidateTest {
String resourcePath = ValidateTest.class.getResource("/patient-uslab-example1.xml").getFile();
ourLog.info(resourcePath);
App.main(new String[] {"validate", "-v", "dstu3", "-p", "-n", resourcePath});
App.main(new String[] {
"validate",
"-v", "dstu3",
"-p",
"-n", resourcePath});
}
@Test
public void testValidateUsingIgPackSucceedingDstu2() {
String resourcePath = ValidateTest.class.getResource("/argo-dstu2-observation-good.json").getFile();
ourLog.info(resourcePath);
App.main(new String[] {
"validate",
"-v", "dstu2",
"-p",
"--igpack", "src/test/resources/argo-dstu2.pack",
"-n", resourcePath});
}
@Test
public void testValidateUsingIgPackFailingDstu2() {
String resourcePath = ValidateTest.class.getResource("/argo-dstu2-observation-bad.json").getFile();
ourLog.info(resourcePath);
try {
App.main(new String[] {
"validate",
"-v", "dstu2",
"-p",
"--igpack", "src/test/resources/argo-dstu2.pack",
"-n", resourcePath});
// Should not get here
fail();
} catch (CommandFailureException e) {
// good
}
}
}

View File

@ -0,0 +1,37 @@
{
"resourceType": "Observation",
"id": "head-circumference",
"meta": {
"profile": [
"http://fhir.org/guides/argonaut/StructureDefinition/argo-vitalsigns"
]
},
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: head-circumference</p><p><b>meta</b>: </p><p><b>status</b>: final</p><p><b>category</b>: Vital Signs <span style=\"background: LightGoldenRodYellow\">(Details : {http://hl7.org/fhir/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})</span></p><p><b>code</b>: head_circumference <span style=\"background: LightGoldenRodYellow\">(Details : {LOINC code '8287-5' = 'Head Occipital-frontal circumference by Tape measure', given as 'head_circumference'})</span></p><p><b>subject</b>: <a href=\"Patient-peter-chalmers.html\">Generated Summary: id: peter-chalmers; Medical Record Number = 1032702; active; Peter James Chalmers (OFFICIAL); ph: (555)555-5555(WORK), person@example.org(WORK); gender: male; birthDate: 19/06/1964</a></p><p><b>encounter</b>: <a href=\"Encounter-691.html\">Generated Summary: id: 691; status: in-progress; class: inpatient</a></p><p><b>effective</b>: 12/08/2010</p><p><b>value</b>: 51.2 cm<span style=\"background: LightGoldenRodYellow\"> (Details: UCUM code cm = 'cm')</span></p></div>"
},
"status": "final",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8287-5",
"display": "head_circumference"
}
],
"text": "head_circumference"
},
"subject": {
"reference": "Patient/peter-chalmers"
},
"encounter": {
"reference": "Encounter/691"
},
"effectiveDateTime": "2010-08-12",
"valueQuantity": {
"value": 51.2,
"unit": "cm",
"system": "http://unitsofmeasure.org",
"code": "cm"
}
}

View File

@ -0,0 +1,47 @@
{
"resourceType": "Observation",
"id": "head-circumference",
"meta": {
"profile": [
"http://fhir.org/guides/argonaut/StructureDefinition/argo-vitalsigns"
]
},
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: head-circumference</p><p><b>meta</b>: </p><p><b>status</b>: final</p><p><b>category</b>: Vital Signs <span style=\"background: LightGoldenRodYellow\">(Details : {http://hl7.org/fhir/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})</span></p><p><b>code</b>: head_circumference <span style=\"background: LightGoldenRodYellow\">(Details : {LOINC code '8287-5' = 'Head Occipital-frontal circumference by Tape measure', given as 'head_circumference'})</span></p><p><b>subject</b>: <a href=\"Patient-peter-chalmers.html\">Generated Summary: id: peter-chalmers; Medical Record Number = 1032702; active; Peter James Chalmers (OFFICIAL); ph: (555)555-5555(WORK), person@example.org(WORK); gender: male; birthDate: 19/06/1964</a></p><p><b>encounter</b>: <a href=\"Encounter-691.html\">Generated Summary: id: 691; status: in-progress; class: inpatient</a></p><p><b>effective</b>: 12/08/2010</p><p><b>value</b>: 51.2 cm<span style=\"background: LightGoldenRodYellow\"> (Details: UCUM code cm = 'cm')</span></p></div>"
},
"status": "final",
"category": {
"coding": [
{
"system": "http://hl7.org/fhir/observation-category",
"code": "vital-signs",
"display": "Vital Signs"
}
],
"text": "Vital Signs"
},
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8287-5",
"display": "head_circumference"
}
],
"text": "head_circumference"
},
"subject": {
"reference": "Patient/peter-chalmers"
},
"encounter": {
"reference": "Encounter/691"
},
"effectiveDateTime": "2010-08-12",
"valueQuantity": {
"value": 51.2,
"unit": "cm",
"system": "http://unitsofmeasure.org",
"code": "cm"
}
}

View File

@ -0,0 +1,159 @@
package ca.uhn.fhir.igpacks.parser;
/*-
* #%L
* hapi-fhir-igpacks
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.ImplementationGuide;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseIgPackParser<T> {
private static final Logger ourLog = LoggerFactory.getLogger(BaseIgPackParser.class);
private final FhirContext myCtx;
public BaseIgPackParser(FhirContext theCtx) {
FhirVersionEnum expectedVersion = provideExpectedVersion();
Validate.isTrue(theCtx.getVersion().getVersion() == expectedVersion, "theCtx is not for the correct version, expecting " + expectedVersion);
myCtx = theCtx;
}
protected abstract T createValidationSupport(Map<IIdType, IBaseResource> theIgResources);
private IBaseResource findResource(Map<String, IBaseResource> theCandidateResources, IIdType theId) {
IBaseResource retVal = theCandidateResources.get(theId.toUnqualifiedVersionless().getValue());
if (retVal == null) {
throw new InternalErrorException("Unknown reference in ImplementationGuide: " + theId);
}
return retVal;
}
/**
* @param theIgInputStream The "validator.pack" ZIP file
* @param theDescription A description (just used for logs)
*/
public T parseIg(InputStream theIgInputStream, String theDescription) {
Validate.notNull(theIgInputStream, "theIdInputStream must not be null");
String igResourceName = "ImplementationGuide-ig.json";
ourLog.info("Parsing IGPack: {}", theDescription);
StopWatch sw = new StopWatch();
ZipInputStream zipInputStream = new ZipInputStream(theIgInputStream);
ZipEntry entry;
try {
Map<String, IBaseResource> candidateResources = new HashMap<>();
Map<IIdType, IBaseResource> igResources = new HashMap<>();
while ((entry = zipInputStream.getNextEntry()) != null) {
if (entry.getName().endsWith(".json")) {
IBaseResource parsed;
InputStreamReader nextReader = new InputStreamReader(zipInputStream, Constants.CHARSET_UTF8);
if (entry.getName().equals(igResourceName)) {
parsed = FhirContext.forDstu3().newJsonParser().parseResource(ImplementationGuide.class, nextReader);
} else {
parsed = myCtx.newJsonParser().parseResource(nextReader);
}
candidateResources.put(entry.getName(), parsed);
}
}
ourLog.info("Parsed {} candidateResources in {}ms", candidateResources.size(), sw.getMillis());
ImplementationGuide ig = (ImplementationGuide) candidateResources.get(igResourceName);
if (ig == null) {
throw new InternalErrorException("IG Pack '" + theDescription + "' does not contain a resource named: " + igResourceName);
}
HashMap<String, IBaseResource> newCandidateResources = new HashMap<>();
for (IBaseResource next : candidateResources.values()) {
newCandidateResources.put(next.getIdElement().toUnqualifiedVersionless().getValue(), next);
}
candidateResources = newCandidateResources;
for (ImplementationGuide.ImplementationGuidePackageComponent nextPackage : ig.getPackage()) {
ourLog.info("Processing package {}", nextPackage.getName());
for (ImplementationGuide.ImplementationGuidePackageResourceComponent nextResource : nextPackage.getResource()) {
if (isNotBlank(nextResource.getSourceReference().getReference())) {
IdType id = new IdType(nextResource.getSourceReference().getReference());
if (isNotBlank(id.getResourceType())) {
switch (id.getResourceType()) {
case "CodeSystem":
case "ConceptMap":
case "StructureDefinition":
case "ValueSet":
IBaseResource resource = findResource(candidateResources, id);
igResources.put(id.toUnqualifiedVersionless(), resource);
break;
}
}
}
}
}
ourLog.info("IG contains {} resources", igResources.size());
return createValidationSupport(igResources);
} catch (Exception e) {
throw new InternalErrorException("Failure while parsing IG: " + e, e);
}
}
/**
* @param theIgPack The "validator.pack" ZIP file
* @param theDescription A description (just used for logs)
*/
public T parseIg(byte[] theIgPack, String theDescription) {
return parseIg(new ByteArrayInputStream(theIgPack), theDescription);
}
protected abstract FhirVersionEnum provideExpectedVersion();
}

View File

@ -0,0 +1,54 @@
package ca.uhn.fhir.igpacks.parser;
/*-
* #%L
* hapi-fhir-igpacks
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.Map;
public class IgPackParserDstu2 extends BaseIgPackParser<IValidationSupport> {
public IgPackParserDstu2(FhirContext theCtx) {
super(massage(theCtx));
}
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
return new IgPackValidationSupportDstu2(theIgResources);
}
protected FhirVersionEnum provideExpectedVersion() {
return FhirVersionEnum.DSTU2_HL7ORG;
}
private static FhirContext massage(FhirContext theCtx) {
if (theCtx.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
return FhirContext.forDstu2Hl7Org();
} else {
return theCtx;
}
}
}

View File

@ -23,119 +23,26 @@ package ca.uhn.fhir.igpacks.parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.ImplementationGuide;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class IgPackParserDstu3 {
private static final Logger ourLog = LoggerFactory.getLogger(IgPackParserDstu3.class);
private final FhirContext myCtx;
public class IgPackParserDstu3 extends BaseIgPackParser<IValidationSupport> {
public IgPackParserDstu3(FhirContext theCtx) {
FhirVersionEnum expectedVersion = FhirVersionEnum.DSTU3;
Validate.isTrue(theCtx.getVersion().getVersion() == expectedVersion, "theCtx is not for the correct version, expecting " + expectedVersion);
myCtx = theCtx;
super(theCtx);
}
private IBaseResource findResource(Map<String, IBaseResource> theCandidateResources, IIdType theId) {
IBaseResource retVal = theCandidateResources.get(theId.toUnqualifiedVersionless().getValue());
if (retVal == null) {
throw new InternalErrorException("Unknown reference in ImplementationGuide: " + theId);
}
return retVal;
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
return new IgPackValidationSupportDstu3(theIgResources);
}
/**
* @param theIgInputStream The "validator.pack" ZIP file
* @param theDescription A description (just used for logs)
*/
public IValidationSupport parseIg(InputStream theIgInputStream, String theDescription) {
Validate.notNull(theIgInputStream, "theIdInputStream must not be null");
ourLog.info("Parsing IGPack: {}", theDescription);
StopWatch sw = new StopWatch();
ZipInputStream zipInputStream = new ZipInputStream(theIgInputStream);
ZipEntry entry;
try {
Map<String, IBaseResource> candidateResources = new HashMap<>();
Map<IIdType, IBaseResource> igResources = new HashMap<>();
while ((entry = zipInputStream.getNextEntry()) != null) {
if (entry.getName().endsWith(".json")) {
InputStreamReader nextReader = new InputStreamReader(zipInputStream, Constants.CHARSET_UTF8);
IBaseResource parsed = myCtx.newJsonParser().parseResource(nextReader);
candidateResources.put(entry.getName(), parsed);
}
}
ourLog.info("Parsed {} candidateResources in {}ms", candidateResources.size(), sw.getMillis());
String igResourceName = "ImplementationGuide-ig.json";
ImplementationGuide ig = (ImplementationGuide) candidateResources.get(igResourceName);
if (ig == null) {
throw new InternalErrorException("IG Pack '" + theDescription + "' does not contain a resource named: " + igResourceName);
}
// ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(ig));
HashMap<String, IBaseResource> newCandidateResources = new HashMap<>();
for (IBaseResource next : candidateResources.values()) {
newCandidateResources.put(next.getIdElement().toUnqualifiedVersionless().getValue(), next);
}
candidateResources = newCandidateResources;
for (ImplementationGuide.ImplementationGuidePackageComponent nextPackage : ig.getPackage()) {
ourLog.info("Processing package {}", nextPackage.getName());
for (ImplementationGuide.ImplementationGuidePackageResourceComponent nextResource : nextPackage.getResource()) {
if (isNotBlank(nextResource.getSourceReference().getReference())) {
IdType id = new IdType(nextResource.getSourceReference().getReference());
if (isNotBlank(id.getResourceType())) {
switch (id.getResourceType()) {
case "CodeSystem":
case "ConceptMap":
case "StructureDefinition":
case "ValueSet":
IBaseResource resource = findResource(candidateResources, id);
igResources.put(id.toUnqualifiedVersionless(), resource);
break;
}
}
}
}
}
ourLog.info("IG contains {} resources", igResources.size());
return new IgPackValidationSupportDstu3(igResources);
} catch (Exception e) {
throw new InternalErrorException("Failure while parsing IG: " + e);
}
protected FhirVersionEnum provideExpectedVersion() {
return FhirVersionEnum.DSTU3;
}
}

View File

@ -0,0 +1,115 @@
package ca.uhn.fhir.igpacks.parser;
/*-
* #%L
* hapi-fhir-igpacks
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
import org.hl7.fhir.instance.model.ConceptMap;
import org.hl7.fhir.instance.model.StructureDefinition;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class IgPackValidationSupportDstu2 implements IValidationSupport {
private final Map<IIdType, IBaseResource> myIgResources;
public IgPackValidationSupportDstu2(Map<IIdType, IBaseResource> theIgResources) {
myIgResources = theIgResources;
}
@Override
public List<StructureDefinition> allStructures() {
ArrayList<StructureDefinition> retVal = new ArrayList<>();
for (Map.Entry<IIdType, IBaseResource> next : myIgResources.entrySet()) {
if (next.getKey().getResourceType().equals("StructureDefinition")) {
retVal.add((StructureDefinition) next.getValue());
}
}
return retVal;
}
@Override
public ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
return null;
}
@Override
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
for (Map.Entry<IIdType, IBaseResource> next : myIgResources.entrySet()) {
if (next.getKey().getResourceType().equals("ValueSet")) {
ValueSet nextVs = (ValueSet) next.getValue();
if (theSystem.equals(nextVs.getUrl())) {
return nextVs;
}
}
}
return null;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
for (Map.Entry<IIdType, IBaseResource> next : myIgResources.entrySet()) {
if (theClass.equals(ConceptMap.class)) {
if (theClass.isAssignableFrom(next.getValue().getClass())) {
ConceptMap sd = ((ConceptMap) next.getValue());
if (sd.getUrl().equals(theUri)) {
return (T) sd;
}
}
}
if (theClass.equals(StructureDefinition.class)) {
if (theClass.isAssignableFrom(next.getValue().getClass())) {
StructureDefinition sd = ((StructureDefinition) next.getValue());
if (sd.getUrl().equals(theUri)) {
return (T) sd;
}
}
}
if (theClass.equals(ValueSet.class)) {
if (theClass.isAssignableFrom(next.getValue().getClass())) {
ValueSet sd = ((ValueSet) next.getValue());
if (sd.getUrl().equals(theUri)) {
return (T) sd;
}
}
}
}
return null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
}

Binary file not shown.

View File

@ -12,6 +12,7 @@ import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.instance.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -74,7 +75,7 @@ public class BaseDstu2Config extends BaseConfig {
@Lazy
public IValidatorModule instanceValidatorDstu2() {
FhirInstanceValidator retVal = new FhirInstanceValidator();
retVal.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
retVal.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
retVal.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), jpaValidationSupportDstu2()));
return retVal;
}

View File

@ -6,6 +6,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import org.hl7.fhir.instance.model.IdType;
import org.hl7.fhir.instance.model.StructureDefinition;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
@ -15,6 +16,8 @@ import org.springframework.beans.factory.annotation.Qualifier;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import java.util.ArrayList;
import java.util.List;
/*
* #%L
@ -61,6 +64,11 @@ public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
@Qualifier("myFhirContextDstu2")
private FhirContext myDstu2Ctx;
@Override
public List<StructureDefinition> allStructures() {
return new ArrayList<>();
}
@Override
@Transactional(value = TxType.SUPPORTS)
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {

View File

@ -23,6 +23,9 @@ package ca.uhn.fhir.rest.server.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -32,6 +35,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* This class is a simple implementation of the resource provider
@ -56,6 +60,11 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
private final String myResourceName;
private Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
private long myNextId;
private AtomicLong myDeleteCount = new AtomicLong(0);
private AtomicLong mySearchCount = new AtomicLong(0);
private AtomicLong myUpdateCount = new AtomicLong(0);
private AtomicLong myCreateCount = new AtomicLong(0);
private AtomicLong myReadCount = new AtomicLong(0);
/**
* Constructor
@ -79,6 +88,17 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
myIdToVersionToResourceMap.clear();
}
/**
* Clear the counts used by {@link #getCountRead()} and other count methods
*/
public void clearCounts() {
myReadCount.set(0L);
myUpdateCount.set(0L);
myCreateCount.set(0L);
myDeleteCount.set(0L);
mySearchCount.set(0L);
}
@Create
public MethodOutcome create(@ResourceParam T theResource) {
long idPart = myNextId++;
@ -87,6 +107,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
IIdType id = store(theResource, idPartAsString, versionIdPart);
myCreateCount.incrementAndGet();
return new MethodOutcome()
.setCreated(true)
.setId(id);
@ -99,13 +121,56 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
throw new ResourceNotFoundException(theId);
}
long nextVersion = versions.lastEntry().getKey() + 1L;
IIdType id = store(null, theId.getIdPart(), nextVersion);
myDeleteCount.incrementAndGet();
return new MethodOutcome()
.setId(id);
}
/**
* This method returns a simple operation count. This is mostly
* useful for testing purposes.
*/
public long getCountCreate() {
return myCreateCount.get();
}
/**
* This method returns a simple operation count. This is mostly
* useful for testing purposes.
*/
public long getCountDelete() {
return myDeleteCount.get();
}
/**
* This method returns a simple operation count. This is mostly
* useful for testing purposes.
*/
public long getCountRead() {
return myReadCount.get();
}
/**
* This method returns a simple operation count. This is mostly
* useful for testing purposes.
*/
public long getCountSearch() {
return mySearchCount.get();
}
/**
* This method returns a simple operation count. This is mostly
* useful for testing purposes.
*/
public long getCountUpdate() {
return myUpdateCount.get();
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return myResourceType;
@ -113,7 +178,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
private synchronized TreeMap<Long, T> getVersionToResource(String theIdPart) {
if (!myIdToVersionToResourceMap.containsKey(theIdPart)) {
myIdToVersionToResourceMap.put(theIdPart, new TreeMap<Long, T>());
myIdToVersionToResourceMap.put(theIdPart, new TreeMap<>());
}
return myIdToVersionToResourceMap.get(theIdPart);
}
@ -125,6 +190,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
throw new ResourceNotFoundException(theId);
}
T retVal;
if (theId.hasVersionIdPart()) {
Long versionId = theId.getVersionIdPartAsLong();
if (!versions.containsKey(versionId)) {
@ -134,24 +200,53 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
if (resource == null) {
throw new ResourceGoneException(theId);
}
return resource;
retVal = resource;
}
} else {
return versions.lastEntry().getValue();
retVal = versions.lastEntry().getValue();
}
myReadCount.incrementAndGet();
return retVal;
}
@Search
public List<IBaseResource> search() {
public List<IBaseResource> search(
@OptionalParam(name = "_id") TokenAndListParam theIds) {
List<IBaseResource> retVal = new ArrayList<>();
for (TreeMap<Long, T> next : myIdToVersionToResourceMap.values()) {
if (next.isEmpty() == false) {
retVal.add(next.lastEntry().getValue());
T nextResource = next.lastEntry().getValue();
boolean matches = true;
if (theIds != null && theIds.getValuesAsQueryTokens().size() > 0) {
for (TokenOrListParam nextIdAnd : theIds.getValuesAsQueryTokens()) {
matches = false;
for (TokenParam nextOr : nextIdAnd.getValuesAsQueryTokens()) {
if (nextOr.getValue().equals(nextResource.getIdElement().getIdPart())) {
matches = true;
}
}
if (!matches) {
break;
}
}
}
if (!matches) {
continue;
}
retVal.add(nextResource);
}
}
mySearchCount.incrementAndGet();
return retVal;
}
@ -188,6 +283,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
IIdType id = store(theResource, idPartAsString, versionIdPart);
myUpdateCount.incrementAndGet();
return new MethodOutcome()
.setCreated(created)
.setId(id);

View File

@ -140,6 +140,8 @@ public class ContactPoint extends Type implements ICompositeType {
return ContactPointSystem.PAGER;
if ("other".equals(codeString))
return ContactPointSystem.OTHER;
if ("url".equals(codeString))
return ContactPointSystem.OTHER;
throw new IllegalArgumentException("Unknown ContactPointSystem code '"+codeString+"'");
}
public Enumeration<ContactPointSystem> fromType(Base code) throws FHIRException {

View File

@ -118,6 +118,8 @@ public class StructureDefinition extends DomainResource {
return StructureDefinitionKind.RESOURCE;
if ("logical".equals(codeString))
return StructureDefinitionKind.LOGICAL;
if ("complex-type".equals(codeString))
return StructureDefinitionKind.DATATYPE;
throw new IllegalArgumentException("Unknown StructureDefinitionKind code '"+codeString+"'");
}
public Enumeration<StructureDefinitionKind> fromType(Base code) throws FHIRException {

View File

@ -21,7 +21,10 @@ import org.junit.BeforeClass;
import org.junit.Test;
import javax.servlet.ServletException;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.Assert.*;
@ -31,11 +34,14 @@ public class HashMapResourceProviderTest {
private static Server ourListenerServer;
private static IGenericClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static HashMapResourceProvider<Patient> myPatientResourceProvider;
private static HashMapResourceProvider<Observation> myObservationResourceProvider;
@Before
public void before() {
ourRestServer.clearData();
myPatientResourceProvider.clearCounts();
myObservationResourceProvider.clearCounts();
}
@Test
@ -50,6 +56,8 @@ public class HashMapResourceProviderTest {
// Read
p = (Patient) ourClient.read().resource("Patient").withId(id).execute();
assertEquals(true, p.getActive());
assertEquals(1, myPatientResourceProvider.getCountRead());
}
@Test
@ -76,8 +84,12 @@ public class HashMapResourceProviderTest {
assertThat(id.getIdPart(), matchesPattern("[0-9]+"));
assertEquals("1", id.getVersionIdPart());
assertEquals(0, myPatientResourceProvider.getCountDelete());
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
assertEquals(1, myPatientResourceProvider.getCountDelete());
// Read
ourClient.read().resource("Patient").withId(id.withVersion("1")).execute();
try {
@ -103,6 +115,56 @@ public class HashMapResourceProviderTest {
Bundle resp = ourClient.search().forResource("Patient").returnBundle(Bundle.class).execute();
assertEquals(100, resp.getTotal());
assertEquals(100, resp.getEntry().size());
assertEquals(1, myPatientResourceProvider.getCountSearch());
}
@Test
public void testSearchById() {
// Create
for (int i = 0; i < 100; i++) {
Patient p = new Patient();
p.addName().setFamily("FAM" + i);
IIdType id = ourClient.create().resource(p).execute().getId();
assertThat(id.getIdPart(), matchesPattern("[0-9]+"));
assertEquals("1", id.getVersionIdPart());
}
// Search
Bundle resp = ourClient
.search()
.forResource("Patient")
.where(Patient.RES_ID.exactly().codes("2", "3"))
.returnBundle(Bundle.class).execute();
assertEquals(2, resp.getTotal());
assertEquals(2, resp.getEntry().size());
List<String> respIds = resp.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
assertThat(respIds, containsInAnyOrder("Patient/2", "Patient/3"));
// Search
resp = ourClient
.search()
.forResource("Patient")
.where(Patient.RES_ID.exactly().codes("2", "3"))
.where(Patient.RES_ID.exactly().codes("2", "3"))
.returnBundle(Bundle.class).execute();
assertEquals(2, resp.getTotal());
assertEquals(2, resp.getEntry().size());
respIds = resp.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
assertThat(respIds, containsInAnyOrder("Patient/2", "Patient/3"));
resp = ourClient
.search()
.forResource("Patient")
.where(Patient.RES_ID.exactly().codes("2", "3"))
.where(Patient.RES_ID.exactly().codes("4", "3"))
.returnBundle(Bundle.class).execute();
respIds = resp.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
assertThat(respIds, containsInAnyOrder("Patient/3"));
assertEquals(1, resp.getTotal());
assertEquals(1, resp.getEntry().size());
}
@Test
@ -122,6 +184,9 @@ public class HashMapResourceProviderTest {
assertThat(id.getIdPart(), matchesPattern("[0-9]+"));
assertEquals("2", id.getVersionIdPart());
assertEquals(1, myPatientResourceProvider.getCountCreate());
assertEquals(1, myPatientResourceProvider.getCountUpdate());
// Read
p = (Patient) ourClient.read().resource("Patient").withId(id.withVersion("1")).execute();
assertEquals(true, p.getActive());
@ -147,6 +212,8 @@ public class HashMapResourceProviderTest {
ourRestServer = new MyRestfulServer();
String ourBase = "http://localhost:" + ourListenerPort + "/";
ourListenerServer = new Server(ourListenerPort);
ourCtx.getRestfulClientFactory().setSocketTimeout(120000);
ourClient = ourCtx.newRestfulGenericClient(ourBase);
ServletContextHandler proxyHandler = new ServletContextHandler();
@ -161,6 +228,7 @@ public class HashMapResourceProviderTest {
}
private static class MyRestfulServer extends RestfulServer {
MyRestfulServer() {
super(ourCtx);
}
@ -177,8 +245,10 @@ public class HashMapResourceProviderTest {
protected void initialize() throws ServletException {
super.initialize();
registerProvider(new HashMapResourceProvider<>(ourCtx, Patient.class));
registerProvider(new HashMapResourceProvider<>(ourCtx, Observation.class));
myPatientResourceProvider = new HashMapResourceProvider<>(ourCtx, Patient.class);
myObservationResourceProvider = new HashMapResourceProvider<>(ourCtx, Observation.class);
registerProvider(myPatientResourceProvider);
registerProvider(myObservationResourceProvider);
}

View File

@ -25,6 +25,19 @@ public class BundleUtilTest {
Assert.assertEquals(null, BundleUtil.getLinkUrlOfType(ourCtx, b, "next"));
}
@Test
public void testGetTotal() {
Bundle b = new Bundle();
b.setTotal(999);
Assert.assertEquals(999, BundleUtil.getTotal(ourCtx, b).intValue());
}
@Test
public void testGetTotalNull() {
Bundle b = new Bundle();
Assert.assertEquals(null, BundleUtil.getTotal(ourCtx, b));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -3,11 +3,8 @@ package org.hl7.fhir.instance.hapi.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.Bundle;
import org.hl7.fhir.instance.model.*;
import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.instance.model.CodeType;
import org.hl7.fhir.instance.model.IdType;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -16,8 +13,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -25,6 +21,13 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
private Map<String, ValueSet> myDefaultValueSets;
private Map<String, ValueSet> myCodeSystems;
private static final Set<String> ourResourceNames;
private static final FhirContext ourHl7OrgCtx;
static {
ourHl7OrgCtx = FhirContext.forDstu2Hl7Org();
ourResourceNames = FhirContext.forDstu2().getResourceNames();
}
/**
* Constructor
@ -33,6 +36,18 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
super();
}
@Override
public List<StructureDefinition> allStructures() {
ArrayList<StructureDefinition> retVal = new ArrayList<>();
for (String next : ourResourceNames) {
StructureDefinition profile = FhirInstanceValidator.loadProfileOrReturnNull(null, ourHl7OrgCtx, next);
retVal.add(profile);
}
return retVal;
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
@ -43,7 +58,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
synchronized (this) {
Map<String, ValueSet> valueSets = myCodeSystems;
if (valueSets == null) {
valueSets = new HashMap<String, ValueSet>();
valueSets = new HashMap<>();
loadValueSets(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/valuesets.xml");
loadValueSets(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
@ -78,7 +93,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
}
defaultValueSets = new HashMap<String, ValueSet>();
defaultValueSets = new HashMap<>();
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader);

View File

@ -33,7 +33,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
@Override
public List<StructureDefinition> allStructures() {
throw new UnsupportedOperationException();
return myValidationSupport.allStructures();
}
@Override

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.instance.hapi.validation;
import org.hl7.fhir.instance.model.StructureDefinition;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
@ -9,9 +10,16 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext;
import java.util.List;
public interface IValidationSupport {
/**
/**
* Fetch all structuredefinitions
*/
List<StructureDefinition> allStructures();
/**
* Expands the given portion of a ValueSet
*
* @param theInclude

View File

@ -1,14 +1,14 @@
package org.hl7.fhir.instance.hapi.validation;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.instance.model.StructureDefinition;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.List;
public class ValidationSupportChain implements IValidationSupport {
@ -37,7 +37,16 @@ public class ValidationSupportChain implements IValidationSupport {
myChain.add(theValidationSupport);
}
@Override
@Override
public List<StructureDefinition> allStructures() {
ArrayList<StructureDefinition> retVal = new ArrayList<>();
for (IValidationSupport next : myChain) {
retVal.addAll(next.allStructures());
}
return retVal;
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) {

View File

@ -2025,6 +2025,7 @@
<module>hapi-fhir-validation-resources-dstu3</module>
<module>hapi-fhir-structures-r4</module>
<module>hapi-fhir-validation-resources-r4</module>
<module>hapi-fhir-igpacks</module>
<module>hapi-fhir-jaxrsserver-base</module>
<module>hapi-fhir-jaxrsserver-example</module>
<module>hapi-fhir-jpaserver-base</module>
@ -2048,7 +2049,6 @@
<module>example-projects/hapi-fhir-jpaserver-dynamic</module>
<module>tests/hapi-fhir-base-test-mindeps-client</module>
<module>tests/hapi-fhir-base-test-mindeps-server</module>
<module>hapi-fhir-igpacks</module>
<module>hapi-fhir-spring-boot</module>
<!--<module>hapi-fhir-osgi-core</module>-->
</modules>

View File

@ -45,6 +45,13 @@
This work was sponsored by the Regenstrief Institute. Thanks
to Regenstrief for their support!
</action>
<action type="add">
The DSTU2 validator has been refactored to use the same codebase
as the DSTU3/R4 validator (which were harmonized in HAPI FHIR 3.3.0).
This means that we now have a single codebase for all validators, which
improves maintainability and brings a number of improvements
to the accuracy of DSTU2 resource validation.
</action>
<action type="fix">
When encoding a resource that had contained resources with user-supplied
local IDs (e.g. resource.setId("#1")) as well as contained resources