From c22aa14d29f8536aa3a387b715a97589a4214541 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 10 Jul 2015 16:05:40 -0400 Subject: [PATCH] Clean up the validation framework and integrate the QuestionnaireAnswers validator --- .../main/java/example/ValidatorExamples.java | 23 +- .../ca/uhn/fhir/validation/FhirValidator.java | 91 +- .../fhir/validation/IValidationContext.java | 17 +- .../fhir/validation/ResultSeverityEnum.java | 51 + .../fhir/validation/SchemaBaseValidator.java | 57 +- .../validation/SchematronBaseValidator.java | 24 +- .../validation/SingleValidationMessage.java | 105 + .../fhir/validation/ValidationContext.java | 137 +- .../uhn/fhir/validation/ValidationResult.java | 100 +- .../validation/ResultSeverityEnumTest.java | 18 + hapi-fhir-jpaserver-base/pom.xml | 6 + ...urceProviderQuestionnaireAnswersDstu2.java | 29 + .../BaseJpaResourceProviderValueSetDstu2.java | 3 + .../provider/ResourceProviderDstu2Test.java | 134 +- .../src/test/resources/extensional-case-2.xml | 11 +- .../validation/ResourceValidatorTest.java | 10 +- .../validation/ValidationResultDstu1Test.java | 64 - .../fhir/rest/server/OperationServerTest.java | 9 + .../validation/ValidationResultDstu2Test.java | 64 - ...idator.java => FhirInstanceValidator.java} | 30 +- .../org/hl7/fhir/instance/model/ValueSet.java | 6 +- .../fhir/instance/utils/WorkerContext.java | 291 +- .../instance/validation/BaseValidator.java | 299 +- .../validation/InstanceValidator.java | 4 +- .../QuestionnaireAnswersValidator.java | 389 ++ .../validation/ValidationMessage.java | 18 + .../ca/uhn/fhir/model/InstantiationTest.java | 26 +- ...st.java => FhirInstanceValidatorTest.java} | 24 +- .../QuestionnaireAnswersValidatorTest.java | 185 + ...swers-0f431c50ddbe4fff8e0dd6b7323625fc.xml | 4016 +++++++++++++++++ .../resources/vm/jpa_resource_provider.vm | 10 +- sync_ri.sh | 1 - 32 files changed, 5638 insertions(+), 614 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ResultSeverityEnum.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SingleValidationMessage.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireAnswersDstu2.java delete mode 100644 hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu1Test.java delete mode 100644 hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu2Test.java rename hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/{StructureDefinitionValidator.java => FhirInstanceValidator.java} (75%) create mode 100644 hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/QuestionnaireAnswersValidator.java rename hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/{StructureDefinitionValidatorTest.java => FhirInstanceValidatorTest.java} (62%) create mode 100644 hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/QuestionnaireAnswersValidatorTest.java create mode 100644 hapi-fhir-structures-hl7org-dstu2/src/test/resources/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml diff --git a/examples/src/main/java/example/ValidatorExamples.java b/examples/src/main/java/example/ValidatorExamples.java index e4ff8725c6e..1bfd4685102 100644 --- a/examples/src/main/java/example/ValidatorExamples.java +++ b/examples/src/main/java/example/ValidatorExamples.java @@ -2,22 +2,26 @@ package example; import java.io.File; import java.io.FileReader; +import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; public class ValidatorExamples { + @SuppressWarnings("unused") public void enableValidation() { // START SNIPPET: enableValidation FhirContext ctx = FhirContext.forDstu2(); @@ -55,13 +59,22 @@ public class ValidatorExamples { } else { // We failed validation! - System.out.println("Validation failed"); - - // The result contains an OperationOutcome outlining the failures - String results = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.getOperationOutcome()); - System.out.println(results); } + + // The result contains a list of "messages" + List messages = result.getMessages(); + for (SingleValidationMessage next : messages) { + System.out.println("Message:"); + System.out.println(" * Location: " + next.getLocationString()); + System.out.println(" * Severity: " + next.getSeverity()); + System.out.println(" * Message : " + next.getMessage()); + } + + // You can also convert the results into an OperationOutcome resource + OperationOutcome oo = (OperationOutcome) result.toOperationOutcome(); + String results = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo); + System.out.println(results); // END SNIPPET: basicValidation } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java index 525fdd16573..070dd81d0a2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java @@ -44,8 +44,8 @@ public class FhirValidator { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirValidator.class); - private static final String I18N_KEY_NO_PHLOC_WARNING = FhirValidator.class.getName()+".noPhlocWarningOnStartup"; - private static final String I18N_KEY_NO_PHLOC_ERROR = FhirValidator.class.getName()+".noPhlocError"; + private static final String I18N_KEY_NO_PHLOC_WARNING = FhirValidator.class.getName() + ".noPhlocWarningOnStartup"; + private static final String I18N_KEY_NO_PHLOC_ERROR = FhirValidator.class.getName() + ".noPhlocError"; private FhirContext myContext; private List myValidators = new ArrayList(); @@ -134,12 +134,12 @@ public class FhirValidator { * Validates a bundle instance, throwing a {@link ValidationFailureException} if the validation fails. This validation includes validation of all resources in the bundle. * * @param theBundle - * The resource to validate + * The resource to validate * @throws ValidationFailureException - * If the validation fails - * @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.Bundle)} instead + * If the validation fails + * @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.Bundle)} instead */ - @Deprecated + @Deprecated public void validate(Bundle theBundle) { Validate.notNull(theBundle, "theBundle must not be null"); @@ -149,7 +149,7 @@ public class FhirValidator { next.validateBundle(ctx); } - IBaseOperationOutcome oo = ctx.getOperationOutcome(); + IBaseOperationOutcome oo = ctx.toResult().toOperationOutcome(); if (oo != null && OperationOutcomeUtil.hasIssues(myContext, oo)) { throw new ValidationFailureException(myContext, oo); } @@ -160,57 +160,56 @@ public class FhirValidator { * Validates a resource instance, throwing a {@link ValidationFailureException} if the validation fails * * @param theResource - * The resource to validate + * The resource to validate * @throws ValidationFailureException - * If the validation fails - * @deprecated use {@link #validateWithResult(IBaseResource)} instead + * If the validation fails + * @deprecated use {@link #validateWithResult(IBaseResource)} instead */ - @Deprecated + @Deprecated public void validate(IResource theResource) throws ValidationFailureException { - ValidationResult validationResult = validateWithResult(theResource); - if (!validationResult.isSuccessful()) { - throw new ValidationFailureException(myContext, validationResult.getOperationOutcome()); - } - } + ValidationResult validationResult = validateWithResult(theResource); + if (!validationResult.isSuccessful()) { + throw new ValidationFailureException(myContext, validationResult.toOperationOutcome()); + } + } - /** - * Validates a bundle instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results. - * This validation includes validation of all resources in the bundle. - * - * @param theBundle the bundle to validate - * @return the results of validation - * @since 0.7 - */ - public ValidationResult validateWithResult(Bundle theBundle) { - Validate.notNull(theBundle, "theBundle must not be null"); + /** + * Validates a bundle instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results. This validation includes validation of all resources in the bundle. + * + * @param theBundle + * the bundle to validate + * @return the results of validation + * @since 0.7 + */ + public ValidationResult validateWithResult(Bundle theBundle) { + Validate.notNull(theBundle, "theBundle must not be null"); - IValidationContext ctx = ValidationContext.forBundle(myContext, theBundle); + IValidationContext ctx = ValidationContext.forBundle(myContext, theBundle); for (IValidator next : myValidators) { next.validateBundle(ctx); } - IBaseOperationOutcome oo = ctx.getOperationOutcome(); - return ValidationResult.valueOf(myContext, oo); - } + return ctx.toResult(); + } - /** - * Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results. - * - * @param theResource the resource to validate - * @return the results of validation - * @since 0.7 - */ - public ValidationResult validateWithResult(IBaseResource theResource) { - Validate.notNull(theResource, "theResource must not be null"); + /** + * Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results. + * + * @param theResource + * the resource to validate + * @return the results of validation + * @since 0.7 + */ + public ValidationResult validateWithResult(IBaseResource theResource) { + Validate.notNull(theResource, "theResource must not be null"); - IValidationContext ctx = ValidationContext.forResource(myContext, theResource); + IValidationContext ctx = ValidationContext.forResource(myContext, theResource); - for (IValidator next : myValidators) { - next.validateResource(ctx); - } + for (IValidator next : myValidators) { + next.validateResource(ctx); + } - IBaseOperationOutcome oo = ctx.getOperationOutcome(); - return ValidationResult.valueOf(myContext, oo); - } + return ctx.toResult(); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java index 6e51e7e0c07..f5285c49768 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java @@ -1,17 +1,22 @@ package ca.uhn.fhir.validation; -import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; - import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.EncodingEnum; interface IValidationContext { - public abstract FhirContext getFhirContext(); + FhirContext getFhirContext(); - public abstract IBaseOperationOutcome getOperationOutcome(); + T getResource(); - public abstract T getResource(); + String getResourceAsString(); - public abstract String getXmlEncodedResource(); + EncodingEnum getResourceAsStringEncoding(); + + String getResourceName(); + + void addValidationMessage(SingleValidationMessage theMessage); + + ValidationResult toResult(); } \ No newline at end of file diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ResultSeverityEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ResultSeverityEnum.java new file mode 100644 index 00000000000..a7a2215203d --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ResultSeverityEnum.java @@ -0,0 +1,51 @@ +package ca.uhn.fhir.validation; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum ResultSeverityEnum { + + /** + * The issue has no relation to the degree of success of the action + */ + INFORMATION("information"), + + /** + * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired + */ + WARNING("warning"), + + /** + * The issue is sufficiently important to cause the action to fail + */ + ERROR("error"), + + /** + * The issue caused the action to fail, and no further checking could be performed + */ + FATAL("fatal"); + + private static Map ourValues; + private String myCode; + + private ResultSeverityEnum(String theCode) { + myCode = theCode; + } + + public String getCode() { + return myCode; + } + + public static ResultSeverityEnum fromCode(String theCode) { + if (ourValues == null) { + HashMap values = new HashMap(); + for (ResultSeverityEnum next : values()) { + values.put(next.getCode(), next); + } + ourValues = Collections.unmodifiableMap(values); + } + return ourValues.get(theCode); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java index eb38ed64782..1873bec27f8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java @@ -49,8 +49,8 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.util.OperationOutcomeUtil; class SchemaBaseValidator implements IValidator { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaBaseValidator.class); @@ -83,10 +83,12 @@ class SchemaBaseValidator implements IValidator { Validator validator = schema.newValidator(); MyErrorHandler handler = new MyErrorHandler(theContext); validator.setErrorHandler(handler); - String encodedResource = theContext.getXmlEncodedResource(); - - // ourLog.info(new FhirContext().newXmlParser().setPrettyPrint(true).encodeBundleToString((Bundle) - // theContext.getResource())); + String encodedResource; + if (theContext.getResourceAsStringEncoding() == EncodingEnum.XML) { + encodedResource = theContext.getResourceAsString(); + } else { + encodedResource = theContext.getFhirContext().newXmlParser().encodeResourceToString((IBaseResource) theContext.getResource()); + } validator.validate(new StreamSource(new StringReader(encodedResource))); } catch (SAXException e) { @@ -106,10 +108,10 @@ class SchemaBaseValidator implements IValidator { return schema; } - Source baseSource = loadXml("dstu", null, theSchemaName); + Source baseSource = loadXml(null, theSchemaName); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - schemaFactory.setResourceResolver(new MyResourceResolver("dstu")); + schemaFactory.setResourceResolver(new MyResourceResolver()); try { schema = schemaFactory.newSchema(new Source[] { baseSource }); @@ -121,7 +123,7 @@ class SchemaBaseValidator implements IValidator { } } - private Source loadXml(String theVersion, String theSystemId, String theSchemaName) { + private Source loadXml(String theSystemId, String theSchemaName) { String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theSchemaName; ourLog.debug("Going to load resource: {}", pathToBase); InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase); @@ -132,17 +134,6 @@ class SchemaBaseValidator implements IValidator { InputStreamReader baseReader = new InputStreamReader(baseIs, Charset.forName("UTF-8")); Source baseSource = new StreamSource(baseReader, theSystemId); - // String schema; - // try { - // schema = IOUtils.toString(baseIs, Charset.forName("UTF-8")); - // } catch (IOException e) { - // throw new InternalErrorException(e); - // } - // - // ourLog.info("Schema is:\n{}", schema); - // - // Source baseSource = new StreamSource(new StringReader(schema), theSystemId); - // Source baseSource = new StreamSource(baseIs, theSystemId); return baseSource; } @@ -168,34 +159,34 @@ class SchemaBaseValidator implements IValidator { myContext = theContext; } - private void addIssue(SAXParseException theException, String theSeverity) { - String details = theException.getLocalizedMessage(); - String location = "Line[" + theException.getLineNumber() + "] Col[" + theException.getColumnNumber() + "]"; - OperationOutcomeUtil.addIssue(myContext.getFhirContext(), myContext.getOperationOutcome(), theSeverity, details, location); + private void addIssue(SAXParseException theException, ResultSeverityEnum theSeverity) { + SingleValidationMessage message = new SingleValidationMessage(); + message.setLocationRow(theException.getLineNumber()); + message.setLocationCol(theException.getColumnNumber()); + message.setMessage(theException.getLocalizedMessage()); + message.setSeverity(theSeverity); + myContext.addValidationMessage(message); } @Override - public void error(SAXParseException theException) throws SAXException { - addIssue(theException, "error"); + public void error(SAXParseException theException) { + addIssue(theException, ResultSeverityEnum.ERROR); } @Override - public void fatalError(SAXParseException theException) throws SAXException { - addIssue(theException, "fatal"); + public void fatalError(SAXParseException theException) { + addIssue(theException, ResultSeverityEnum.FATAL); } @Override - public void warning(SAXParseException theException) throws SAXException { - addIssue(theException, "warning"); + public void warning(SAXParseException theException) { + addIssue(theException, ResultSeverityEnum.WARNING); } } private final class MyResourceResolver implements LSResourceResolver { - private String myVersion; - - private MyResourceResolver(String theVersion) { - myVersion = theVersion; + private MyResourceResolver() { } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java index 353826d0d70..080065f8aa0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java @@ -35,7 +35,6 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.util.OperationOutcomeUtil; import com.phloc.commons.error.IResourceError; import com.phloc.commons.error.IResourceErrorGroup; @@ -56,7 +55,7 @@ public class SchematronBaseValidator implements IValidator { public void validateResource(IValidationContext theCtx) { ISchematronResource sch = getSchematron(theCtx); - StreamSource source = new StreamSource(new StringReader(theCtx.getXmlEncodedResource())); + StreamSource source = new StreamSource(new StringReader(theCtx.getResourceAsString())); SchematronOutputType results = SchematronHelper.applySchematron(sch, source); if (results == null) { @@ -70,16 +69,16 @@ public class SchematronBaseValidator implements IValidator { } for (IResourceError next : errors.getAllErrors().getAllResourceErrors()) { - String severity; + ResultSeverityEnum severity; switch (next.getErrorLevel()) { case ERROR: - severity = ("error"); + severity = ResultSeverityEnum.ERROR; break; case FATAL_ERROR: - severity = ("fatal"); + severity = ResultSeverityEnum.FATAL; break; case WARN: - severity = ("warning"); + severity = ResultSeverityEnum.WARNING; break; case INFO: case SUCCESS: @@ -88,7 +87,14 @@ public class SchematronBaseValidator implements IValidator { } String details = next.getAsString(Locale.getDefault()); - OperationOutcomeUtil.addIssue(myCtx, theCtx.getOperationOutcome(), severity, details); + + SingleValidationMessage message = new SingleValidationMessage(); + message.setMessage(details); + message.setLocationRow(next.getLocation().getLineNumber()); + message.setLocationCol(next.getLocation().getColumnNumber()); + message.setLocationString(next.getLocation().getAsString()); + message.setSeverity(severity); + theCtx.addValidationMessage(message); } } @@ -97,10 +103,10 @@ public class SchematronBaseValidator implements IValidator { Class resource = theCtx.getResource().getClass(); Class baseResourceClass = theCtx.getFhirContext().getResourceDefinition(resource).getBaseDefinition().getImplementingClass(); - return getSchematronAndCache(theCtx, "dstu", baseResourceClass); + return getSchematronAndCache(theCtx, baseResourceClass); } - private ISchematronResource getSchematronAndCache(IValidationContext theCtx, String theVersion, Class theClass) { + private ISchematronResource getSchematronAndCache(IValidationContext theCtx, Class theClass) { synchronized (myClassToSchematron) { ISchematronResource retVal = myClassToSchematron.get(theClass); if (retVal != null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SingleValidationMessage.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SingleValidationMessage.java new file mode 100644 index 00000000000..60bb2c77ac2 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SingleValidationMessage.java @@ -0,0 +1,105 @@ +package ca.uhn.fhir.validation; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class SingleValidationMessage { + + private Integer myLocationCol; + private Integer myLocationRow; + private String myLocationString; + private String myMessage; + private ResultSeverityEnum mySeverity; + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SingleValidationMessage)) { + return false; + } + SingleValidationMessage other = (SingleValidationMessage) obj; + EqualsBuilder b = new EqualsBuilder(); + b.append(myLocationCol, other.myLocationCol); + b.append(myLocationRow, other.myLocationRow); + b.append(myLocationString, other.myLocationString); + b.append(myMessage, other.myMessage); + b.append(mySeverity, other.mySeverity); + return b.isEquals(); + } + + public Integer getLocationCol() { + return myLocationCol; + } + + public Integer getLocationRow() { + return myLocationRow; + } + + public String getLocationString() { + return myLocationString; + } + + public String getMessage() { + return myMessage; + } + + public ResultSeverityEnum getSeverity() { + return mySeverity; + } + + @Override + public int hashCode() { + HashCodeBuilder b = new HashCodeBuilder(); + b.append(myLocationCol); + b.append(myLocationCol); + b.append(myLocationString); + b.append(myMessage); + b.append(mySeverity); + return b.toHashCode(); + } + + public void setLocationCol(Integer theLocationCol) { + myLocationCol = theLocationCol; + } + + public void setLocationRow(Integer theLocationRow) { + myLocationRow = theLocationRow; + } + + public void setLocationString(String theLocationString) { + myLocationString = theLocationString; + } + + public void setMessage(String theMessage) { + myMessage = theMessage; + } + + public void setSeverity(ResultSeverityEnum theSeverity) { + mySeverity = theSeverity; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + if (myLocationCol != null || myLocationRow != null) { + b.append("myLocationCol", myLocationCol); + b.append("myLocationRow", myLocationRow); + } + if (myLocationString != null) { + b.append("myLocationString", myLocationString); + } + b.append("myMessage", myMessage); + if (mySeverity != null) { + b.append("mySeverity", mySeverity.getCode()); + } + return b.toString(); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java index 7f7485f5d7b..47c1b72aa48 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java @@ -20,97 +20,152 @@ package ca.uhn.fhir.validation; * #L% */ -import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; +import java.util.ArrayList; +import java.util.List; + import org.hl7.fhir.instance.model.api.IBaseResource; +import org.thymeleaf.util.Validate; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.rest.server.EncodingEnum; class ValidationContext implements IValidationContext { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationContext.class); private final IEncoder myEncoder; private final FhirContext myFhirContext; - private IBaseOperationOutcome myOperationOutcome; + private List myMessages = new ArrayList(); private final T myResource; - private String myXmlEncodedResource; + private String myResourceAsString; + private final EncodingEnum myResourceAsStringEncoding; + private final String myResourceName; - private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder) { + private ValidationContext(FhirContext theContext, T theResource, String theResourceName, IEncoder theEncoder) { myFhirContext = theContext; myResource = theResource; myEncoder = theEncoder; + myResourceName = theResourceName; + if (theEncoder != null) { + myResourceAsStringEncoding = theEncoder.getEncoding(); + } else { + myResourceAsStringEncoding = null; + } + } + + @Override + public void addValidationMessage(SingleValidationMessage theMessage) { + Validate.notNull(theMessage, "theMessage must not be null"); + myMessages.add(theMessage); } - /* (non-Javadoc) - * @see ca.uhn.fhir.validation.IValidationContext#getFhirContext() - */ @Override public FhirContext getFhirContext() { return myFhirContext; } - /* (non-Javadoc) - * @see ca.uhn.fhir.validation.IValidationContext#getOperationOutcome() - */ - @Override - public IBaseOperationOutcome getOperationOutcome() { - if (myOperationOutcome == null) { - try { - myOperationOutcome = (IBaseOperationOutcome) myFhirContext.getResourceDefinition("OperationOutcome").getImplementingClass().newInstance(); - } catch (Exception e1) { - ourLog.error("Failed to instantiate OperationOutcome resource instance", e1); - throw new InternalErrorException("Failed to instantiate OperationOutcome resource instance", e1); - } - } - return myOperationOutcome; - } - - /* (non-Javadoc) - * @see ca.uhn.fhir.validation.IValidationContext#getResource() - */ @Override public T getResource() { return myResource; } - /* (non-Javadoc) - * @see ca.uhn.fhir.validation.IValidationContext#getXmlEncodedResource() - */ @Override - public String getXmlEncodedResource() { - if (myXmlEncodedResource == null) { - myXmlEncodedResource = myEncoder.encode(); + public String getResourceAsString() { + if (myResourceAsString == null) { + myResourceAsString = myEncoder.encode(); } - return myXmlEncodedResource; + return myResourceAsString; + } + + @Override + public EncodingEnum getResourceAsStringEncoding() { + return myResourceAsStringEncoding; + } + + @Override + public String getResourceName() { + return myResourceName; + } + + @Override + public ValidationResult toResult() { + return new ValidationResult(myFhirContext, myMessages); } public static IValidationContext forBundle(final FhirContext theContext, final Bundle theBundle) { - return new ValidationContext(theContext, theBundle, new IEncoder() { + String resourceName = "Bundle"; + return new ValidationContext(theContext, theBundle, resourceName, new IEncoder() { @Override public String encode() { return theContext.newXmlParser().encodeBundleToString(theBundle); } + + @Override + public EncodingEnum getEncoding() { + return EncodingEnum.XML; + } }); } public static IValidationContext forResource(final FhirContext theContext, final T theResource) { - return new ValidationContext(theContext, theResource, new IEncoder() { + String resourceName = theContext.getResourceDefinition(theResource).getName(); + return new ValidationContext(theContext, theResource, resourceName, new IEncoder() { @Override public String encode() { return theContext.newXmlParser().encodeResourceToString(theResource); } + + @Override + public EncodingEnum getEncoding() { + return EncodingEnum.XML; + } }); } - public static IValidationContext newChild(IValidationContext theContext, IBaseResource theResource) { - ValidationContext retVal = (ValidationContext) forResource(theContext.getFhirContext(), theResource); - retVal.myOperationOutcome = theContext.getOperationOutcome(); - return retVal; + public static IValidationContext newChild(final IValidationContext theContext, final IResource theResource) { + return new IValidationContext() { + + @Override + public void addValidationMessage(SingleValidationMessage theMessage) { + theContext.addValidationMessage(theMessage); + } + + @Override + public FhirContext getFhirContext() { + return theContext.getFhirContext(); + } + + @Override + public IBaseResource getResource() { + return theResource; + } + + @Override + public String getResourceAsString() { + return theContext.getFhirContext().newXmlParser().encodeResourceToString(theResource); + } + + @Override + public EncodingEnum getResourceAsStringEncoding() { + return EncodingEnum.XML; + } + + @Override + public String getResourceName() { + return theContext.getFhirContext().getResourceDefinition(theResource).getName(); + } + + @Override + public ValidationResult toResult() { + return theContext.toResult(); + } + }; } private interface IEncoder { String encode(); + + EncodingEnum getEncoding(); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java index 3d4cea1d709..a69cf3b0467 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java @@ -20,6 +20,11 @@ package ca.uhn.fhir.validation; * #L% */ +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import java.util.Collections; +import java.util.List; + import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import ca.uhn.fhir.context.FhirContext; @@ -32,40 +37,25 @@ import ca.uhn.fhir.util.OperationOutcomeUtil; * @since 0.7 */ public class ValidationResult { - private IBaseOperationOutcome myOperationOutcome; - private boolean myIsSuccessful; - private FhirContext myCtx; + private final FhirContext myCtx; + private final boolean myIsSuccessful; + private final List myMessages; - private ValidationResult(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, boolean isSuccessful) { - this.myCtx = theCtx; - this.myOperationOutcome = theOperationOutcome; - this.myIsSuccessful = isSuccessful; - } - - public static ValidationResult valueOf(FhirContext theCtx, IBaseOperationOutcome myOperationOutcome) { - boolean noIssues = !OperationOutcomeUtil.hasIssues(theCtx, myOperationOutcome); - return new ValidationResult(theCtx, myOperationOutcome, noIssues); - } - - public IBaseOperationOutcome getOperationOutcome() { - return myOperationOutcome; - } - - @Override - public String toString() { - return "ValidationResult{" + "myOperationOutcome=" + myOperationOutcome + ", isSuccessful=" + myIsSuccessful + ", description='" + toDescription() + '\'' + '}'; - } - - private String toDescription() { - StringBuilder b = new StringBuilder(100); - if (myOperationOutcome != null && OperationOutcomeUtil.hasIssues(myCtx, myOperationOutcome)) { - b.append(OperationOutcomeUtil.getFirstIssueDetails(myCtx, myOperationOutcome)); - b.append(" - "); - b.append(OperationOutcomeUtil.getFirstIssueLocation(myCtx, myOperationOutcome)); - } else { - b.append("No issues"); + public ValidationResult(FhirContext theCtx, List theMessages) { + boolean successful = true; + myCtx = theCtx; + myMessages = theMessages; + for (SingleValidationMessage next : myMessages) { + next.getSeverity(); + if (next.getSeverity() == null || next.getSeverity().ordinal() > ResultSeverityEnum.WARNING.ordinal()) { + successful = false; + } } - return b.toString(); + myIsSuccessful = successful; + } + + public List getMessages() { + return Collections.unmodifiableList(myMessages); } /** @@ -76,4 +66,50 @@ public class ValidationResult { public boolean isSuccessful() { return myIsSuccessful; } + + private String toDescription() { + StringBuilder b = new StringBuilder(100); + if (myMessages.size() > 0) { + b.append(myMessages.get(0).getMessage()); + b.append(" - "); + b.append(myMessages.get(0).getLocationString()); + } else { + b.append("No issues"); + } + return b.toString(); + } + + /** + * @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view + */ + @Deprecated + public IBaseOperationOutcome getOperationOutcome() { + return toOperationOutcome(); + } + + /** + * Create an OperationOutcome resource which contains all of the messages found as a result of this validation + */ + public IBaseOperationOutcome toOperationOutcome() { + IBaseOperationOutcome oo = (IBaseOperationOutcome) myCtx.getResourceDefinition("OperationOutcome").newInstance(); + for (SingleValidationMessage next : myMessages) { + String location; + if (isNotBlank(next.getLocationString())) { + location = next.getLocationString(); + } else if (next.getLocationRow() != null || next.getLocationCol() != null) { + location = "Line[" + next.getLocationRow() + "] Col[" + next.getLocationCol() + "]"; + } else { + location = null; + } + String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null; + OperationOutcomeUtil.addIssue(myCtx, oo, severity, next.getMessage(), location); + } + + return oo; + } + + @Override + public String toString() { + return "ValidationResult{" + "messageCount=" + myMessages.size() + ", isSuccessful=" + myIsSuccessful + ", description='" + toDescription() + '\'' + '}'; + } } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java new file mode 100644 index 00000000000..9ad7f47df3d --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.validation; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class ResultSeverityEnumTest { + + @Test + public void testOrdinals() { + assertEquals(0, ResultSeverityEnum.INFORMATION.ordinal()); + assertEquals(1, ResultSeverityEnum.WARNING.ordinal()); + assertEquals(2, ResultSeverityEnum.ERROR.ordinal()); + assertEquals(3, ResultSeverityEnum.FATAL.ordinal()); + + } + +} diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 1a20f09843d..240d040a790 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -51,6 +51,12 @@ 1.1-SNAPSHOT true + + ca.uhn.hapi.fhir + hapi-fhir-structures-hl7org-dstu2 + 1.1-SNAPSHOT + true + org.slf4j diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireAnswersDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireAnswersDstu2.java new file mode 100644 index 00000000000..a1b95c44417 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireAnswersDstu2.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.provider; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2015 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.model.dstu2.resource.Encounter; + +public class BaseJpaResourceProviderQuestionnaireAnswersDstu2 extends JpaResourceProviderDstu2 { + + // nothing yet + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java index 824a76db568..f740b0484b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java @@ -87,6 +87,9 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst } else { String filter = theFilter.getValue().toLowerCase(); if (next.getDisplay().toLowerCase().contains(filter) || next.getCode().toLowerCase().contains(filter)) { + if (include == null) { + include = getOrAddComposeInclude(retVal, systemToCompose, source.getDefine().getSystem()); + } include.addConcept(new ComposeIncludeConcept().setCode(next.getCode()).setDisplay(next.getDisplay())); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index e78fea4438b..55e59e6a44b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -340,8 +340,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest { } /* - * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to - * make sure that works too.. + * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too.. */ Socket sock = new Socket(); sock.setSoTimeout(3000); @@ -715,7 +714,8 @@ public class ResourceProviderDstu2Test extends BaseJpaTest { p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01"); IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId(); - Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute(); + Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint() + .execute(); assertEquals(1, actual.size()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); @@ -1141,7 +1141,8 @@ public class ResourceProviderDstu2Test extends BaseJpaTest { assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history")); - Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")).encodedJson().prettyPrint().execute(); + Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")) + .encodedJson().prettyPrint().execute(); assertEquals(1, actual.size()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); @@ -1174,54 +1175,6 @@ public class ResourceProviderDstu2Test extends BaseJpaTest { } } -// @Test - public void testValueSetExpandOperartion() throws IOException { - - ValueSet upload = ourCtx.newXmlParser().parseResource(ValueSet.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/extensional-case-2.xml"))); - IIdType vsid = ourClient.create().resource(upload).execute().getId().toUnqualifiedVersionless(); - - HttpGet get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand"); - CloseableHttpResponse response = ourHttpClient.execute(get); - try { - String resp = IOUtils.toString(response.getEntity().getContent()); - ourLog.info(resp); - assertEquals(200, response.getStatusLine().getStatusCode()); - //@formatter:on - assertThat(resp, stringContainsInOrder( - "", - "" , - "" , - "" , - "" , - "" , - "" , - "")); - //@formatter:off - } finally { - IOUtils.closeQuietly(response.getEntity().getContent()); - response.close(); - } - - /* - * Filter - */ - - get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand?filter=systolic"); - response = ourHttpClient.execute(get); - try { - String resp = IOUtils.toString(response.getEntity().getContent()); - ourLog.info(resp); - assertEquals(200, response.getStatusLine().getStatusCode()); - assertThat(resp, stringContainsInOrder( - "" , - "")); - } finally { - IOUtils.closeQuietly(response.getEntity().getContent()); - response.close(); - } - - } - @Test public void testValidateResourceHuge() throws IOException { @@ -1277,6 +1230,83 @@ public class ResourceProviderDstu2Test extends BaseJpaTest { } } + @Test + public void testValueSetExpandOperation() throws IOException { + + ValueSet upload = ourCtx.newXmlParser().parseResource(ValueSet.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/extensional-case-2.xml"))); + IIdType vsid = ourClient.create().resource(upload).execute().getId().toUnqualifiedVersionless(); + + HttpGet get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand"); + CloseableHttpResponse response = ourHttpClient.execute(get); + try { + String resp = IOUtils.toString(response.getEntity().getContent()); + ourLog.info(resp); + assertEquals(200, response.getStatusLine().getStatusCode()); + // @formatter:off + assertThat( + resp, + stringContainsInOrder("", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + )); + //@formatter:on + } finally { + IOUtils.closeQuietly(response.getEntity().getContent()); + response.close(); + } + + /* + * Filter with display name + */ + + get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand?filter=systolic"); + response = ourHttpClient.execute(get); + try { + String resp = IOUtils.toString(response.getEntity().getContent()); + ourLog.info(resp); + assertEquals(200, response.getStatusLine().getStatusCode()); + //@formatter:off + assertThat(resp, stringContainsInOrder( + "", + "")); + //@formatter:on + } finally { + IOUtils.closeQuietly(response.getEntity().getContent()); + response.close(); + } + + /* + * Filter with code + */ + + get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand?filter=11378"); + response = ourHttpClient.execute(get); + try { + String resp = IOUtils.toString(response.getEntity().getContent()); + ourLog.info(resp); + assertEquals(200, response.getStatusLine().getStatusCode()); + //@formatter:off + assertThat(resp, stringContainsInOrder( + "", + "" + )); + //@formatter:on + } finally { + IOUtils.closeQuietly(response.getEntity().getContent()); + response.close(); + } + + } + private List toIdListUnqualifiedVersionless(Bundle found) { List list = new ArrayList(); for (BundleEntry next : found.getEntries()) { diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.xml index 98d0f8831d5..1f0fa1ef361 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.xml @@ -19,6 +19,13 @@ + + + + + + + @@ -38,10 +45,6 @@ - - - - diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java index 47c04ce735a..40c9ebd71b9 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java @@ -84,10 +84,10 @@ public class ResourceValidatorTest { assertEquals("2001-03-31", new SimpleDateFormat("yyyy-MM-dd").format(p.getBirthDate().getValue())); ValidationResult result = ourCtx.newValidator().validateWithResult(p); - String resultString = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.getOperationOutcome()); + String resultString = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()); ourLog.info(resultString); - assertEquals(2, ((OperationOutcome)result.getOperationOutcome()).getIssue().size()); + assertEquals(2, ((OperationOutcome)result.toOperationOutcome()).getIssue().size()); assertThat(resultString, StringContains.containsString("cvc-datatype-valid.1.2.3")); } @@ -129,7 +129,7 @@ public class ResourceValidatorTest { p.getTelecomFirstRep().setValue("123-4567"); validationResult = val.validateWithResult(p); assertFalse(validationResult.isSuccessful()); - OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome(); + OperationOutcome operationOutcome = (OperationOutcome) validationResult.toOperationOutcome(); ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome)); assertEquals(1, operationOutcome.getIssue().size()); assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2:")); @@ -147,7 +147,7 @@ public class ResourceValidatorTest { FhirValidator val = createFhirValidator(); ValidationResult result = val.validateWithResult(b); - OperationOutcome operationOutcome = (OperationOutcome) result.getOperationOutcome(); + OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome(); ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome)); @@ -170,7 +170,7 @@ public class ResourceValidatorTest { p.getTelecomFirstRep().setValue("123-4567"); validationResult = val.validateWithResult(b); assertFalse(validationResult.isSuccessful()); - OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome(); + OperationOutcome operationOutcome = (OperationOutcome) validationResult.toOperationOutcome(); ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome)); assertEquals(1, operationOutcome.getIssue().size()); assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2:")); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu1Test.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu1Test.java deleted file mode 100644 index b60edc0d4b3..00000000000 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu1Test.java +++ /dev/null @@ -1,64 +0,0 @@ -package ca.uhn.fhir.validation; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue; -import ca.uhn.fhir.model.dstu.resource.OperationOutcome; - -import org.junit.Test; - -import java.util.List; -import java.util.UUID; - -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -public class ValidationResultDstu1Test { - - private static final FhirContext ourCtx = FhirContext.forDstu1(); - - @Test - public void isSuccessful_FalseForIssues() { - OperationOutcome operationOutcome = new OperationOutcome(); - OperationOutcome.Issue issue = operationOutcome.addIssue(); - String errorMessage = "There was a validation problem"; - issue.setDetails(errorMessage); - ValidationResult result = ValidationResult.valueOf(ourCtx, operationOutcome); - assertFalse(result.isSuccessful()); - List issues = ((OperationOutcome) result.getOperationOutcome()).getIssue(); - assertEquals(1, issues.size()); - assertEquals(errorMessage, issues.get(0).getDetailsElement().getValue()); - - assertThat("ValidationResult#toString should contain the issue description", result.toString(), containsString(errorMessage)); - } - - @Test - public void isSuccessful_IsTrueForNoIssues() { - OperationOutcome operationOutcome = new OperationOutcome(); - // make sure a non-null ID doesn't cause the validation result to be a fail - operationOutcome.setId(UUID.randomUUID().toString()); - ValidationResult result = ValidationResult.valueOf(ourCtx, operationOutcome); - assertTrue(result.isSuccessful()); - } - - @Test - public void isSuccessful_IsTrueForNullOperationOutcome() { - ValidationResult result = ValidationResult.valueOf(ourCtx, null); - assertTrue(result.isSuccessful()); - } - - /* - * Test for https://github.com/jamesagnew/hapi-fhir/issues/51 - */ - @Test - public void toString_ShouldNotCauseResultToBecomeFailure() { - OperationOutcome operationOutcome = new OperationOutcome(); - ValidationResult result = ValidationResult.valueOf(ourCtx, operationOutcome); - assertEquals(true, result.isSuccessful()); - // need to call toString to make sure any unwanted side effects are generated - result.toString(); - assertEquals(true, result.isSuccessful()); - } -} diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java index 48249943126..2ac55c23945 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.StringDt; @@ -68,6 +69,14 @@ public class OperationServerTest { ourLastMethod = ""; } + public static void main(String[] theValue) { + Parameters p = new Parameters(); + p.addParameter().setName("start").setValue(new DateTimeDt("2001-01-02")); + p.addParameter().setName("end").setValue(new DateTimeDt("2015-07-10")); + String inParamsStr = FhirContext.forDstu2().newXmlParser().encodeResourceToString(p); + ourLog.info(inParamsStr.replace("\"", "\\\"")); + } + @Test public void testOperationOnType() throws Exception { Parameters p = new Parameters(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu2Test.java deleted file mode 100644 index 84fe118ee0d..00000000000 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ValidationResultDstu2Test.java +++ /dev/null @@ -1,64 +0,0 @@ -package ca.uhn.fhir.validation; - -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.util.List; -import java.util.UUID; - -import org.junit.Test; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue; -import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; - -public class ValidationResultDstu2Test { - - private static final FhirContext ourCtx = FhirContext.forDstu2(); - - @Test - public void isSuccessful_IsTrueForNullOperationOutcome() { - ValidationResult result = ValidationResult.valueOf(ourCtx, null); - assertTrue(result.isSuccessful()); - } - - @Test - public void isSuccessful_IsTrueForNoIssues() { - OperationOutcome operationOutcome = new OperationOutcome(); - // make sure a non-null ID doesn't cause the validation result to be a fail - operationOutcome.setId(UUID.randomUUID().toString()); - ValidationResult result = ValidationResult.valueOf(ourCtx, operationOutcome); - assertTrue(result.isSuccessful()); - } - - @Test - public void isSuccessful_FalseForIssues() { - OperationOutcome operationOutcome = new OperationOutcome(); - OperationOutcome.Issue issue = operationOutcome.addIssue(); - String errorMessage = "There was a validation problem"; - issue.setDetails(errorMessage); - ValidationResult result = ValidationResult.valueOf(ourCtx, operationOutcome); - assertFalse(result.isSuccessful()); - List issues = ((OperationOutcome)result.getOperationOutcome()).getIssue(); - assertEquals(1, issues.size()); - assertEquals(errorMessage, issues.get(0).getDetailsElement().getValue()); - - assertThat("ValidationResult#toString should contain the issue description", result.toString(), containsString(errorMessage)); - } - - /* - Test for https://github.com/jamesagnew/hapi-fhir/issues/51 - */ - @Test - public void toString_ShouldNotCauseResultToBecomeFailure() { - OperationOutcome operationOutcome = new OperationOutcome(); - ValidationResult result = ValidationResult.valueOf(ourCtx, operationOutcome); - assertEquals(true, result.isSuccessful()); - // need to call toString to make sure any unwanted side effects are generated - @SuppressWarnings("UnusedDeclaration") String unused = result.toString(); - assertEquals(true, result.isSuccessful()); - } -} diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/StructureDefinitionValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/FhirInstanceValidator.java similarity index 75% rename from hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/StructureDefinitionValidator.java rename to hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/FhirInstanceValidator.java index 7456a69eb9e..5cc01243aea 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/StructureDefinitionValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/FhirInstanceValidator.java @@ -23,24 +23,22 @@ import com.google.gson.JsonObject; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -public class StructureDefinitionValidator { +public class FhirInstanceValidator implements IValidator { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StructureDefinitionValidator.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class); - private FhirContext myCtx; private DocumentBuilderFactory myDocBuilderFactory; - StructureDefinitionValidator(FhirContext theContext) { - myCtx = theContext; - + FhirInstanceValidator() { myDocBuilderFactory = DocumentBuilderFactory.newInstance(); myDocBuilderFactory.setNamespaceAware(true); } - public List validate(String theInput, EncodingEnum theEncoding, Class theResourceType) { + List validate(FhirContext theCtx, String theInput, EncodingEnum theEncoding, String theResourceName) { WorkerContext workerContext = new WorkerContext(); org.hl7.fhir.instance.validation.InstanceValidator v; try { @@ -49,14 +47,14 @@ public class StructureDefinitionValidator { throw new ConfigurationException(e); } - String profileCpName = "/org/hl7/fhir/instance/model/profile/" + myCtx.getResourceDefinition(theResourceType).getName().toLowerCase() + ".profile.xml"; + String profileCpName = "/org/hl7/fhir/instance/model/profile/" + theResourceName.toLowerCase() + ".profile.xml"; String profileText; try { - profileText = IOUtils.toString(StructureDefinitionValidator.class.getResourceAsStream(profileCpName), "UTF-8"); + profileText = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream(profileCpName), "UTF-8"); } catch (IOException e1) { throw new ConfigurationException("Failed to load profile from classpath: " + profileCpName, e1); } - StructureDefinition profile = myCtx.newXmlParser().parseResource(StructureDefinition.class, profileText); + StructureDefinition profile = theCtx.newXmlParser().parseResource(StructureDefinition.class, profileText); if (theEncoding == EncodingEnum.XML) { Document document; @@ -91,4 +89,16 @@ public class StructureDefinitionValidator { } + @Override + public void validateResource(IValidationContext theCtx) { + String resourceName = theCtx.getResourceName(); + String resourceBody = theCtx.getResourceAsString(); + validate(theCtx.getFhirContext(), resourceBody, theCtx.getResourceAsStringEncoding(), resourceName); + } + + @Override + public void validateBundle(IValidationContext theContext) { + + } + } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/ValueSet.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/ValueSet.java index bf4a967c87a..b7c1f496037 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/ValueSet.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/ValueSet.java @@ -624,7 +624,7 @@ public class ValueSet extends DomainResource { /** * If this code is not for use as a real concept. */ - @Child(name = "abstract_", type = {BooleanType.class}, order=2, min=0, max=1) + @Child(name = "abstract", type = {BooleanType.class}, order=2, min=0, max=1) @Description(shortDefinition="If this code is not for use as a real concept", formalDefinition="If this code is not for use as a real concept." ) protected BooleanType abstract_; @@ -1210,7 +1210,7 @@ public class ValueSet extends DomainResource { /** * Includes the contents of the referenced value set as a part of the contents of this value set. This is an absolute URI that is a reference to ValueSet.uri. */ - @Child(name = "import_", type = {UriType.class}, order=1, min=0, max=Child.MAX_UNLIMITED) + @Child(name = "import", type = {UriType.class}, order=1, min=0, max=Child.MAX_UNLIMITED) @Description(shortDefinition="Import the contents of another value set", formalDefinition="Includes the contents of the referenced value set as a part of the contents of this value set. This is an absolute URI that is a reference to ValueSet.uri." ) protected List import_; @@ -2757,7 +2757,7 @@ public class ValueSet extends DomainResource { /** * If true, this entry is included in the expansion for navigational purposes, and the user cannot select the code directly as a proper value. */ - @Child(name = "abstract_", type = {BooleanType.class}, order=2, min=0, max=1) + @Child(name = "abstract", type = {BooleanType.class}, order=2, min=0, max=1) @Description(shortDefinition="If user cannot select this entry", formalDefinition="If true, this entry is included in the expansion for navigational purposes, and the user cannot select the code directly as a proper value." ) protected BooleanType abstract_; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utils/WorkerContext.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utils/WorkerContext.java index e7436f72ac3..b35307d71db 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utils/WorkerContext.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utils/WorkerContext.java @@ -17,6 +17,7 @@ import org.hl7.fhir.instance.model.Conformance; import org.hl7.fhir.instance.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.instance.model.OperationOutcome; import org.hl7.fhir.instance.model.Parameters; +import org.hl7.fhir.instance.model.Questionnaire; import org.hl7.fhir.instance.model.Resource; import org.hl7.fhir.instance.model.StructureDefinition; import org.hl7.fhir.instance.model.ValueSet; @@ -28,27 +29,27 @@ import org.hl7.fhir.instance.terminologies.ValueSetExpander.ValueSetExpansionOut /* * private static Map loadProfiles() throws Exception { - HashMap result = new HashMap(); - Bundle feed = new XmlParser().parseGeneral(new FileInputStream(PROFILES)).getFeed(); - for (AtomEntry e : feed.getEntryList()) { - if (e.getReference() instanceof StructureDefinition) { - result.put(e.getId(), (StructureDefinition) e.getReference()); - } - } - return result; - } + HashMap result = new HashMap(); + Bundle feed = new XmlParser().parseGeneral(new FileInputStream(PROFILES)).getFeed(); + for (AtomEntry e : feed.getEntryList()) { + if (e.getReference() instanceof StructureDefinition) { + result.put(e.getId(), (StructureDefinition) e.getReference()); + } + } + return result; + } - private static final String TEST_PROFILE = "C:\\work\\org.hl7.fhir\\build\\publish\\namespace.profile.xml"; - private static final String PROFILES = "C:\\work\\org.hl7.fhir\\build\\publish\\profiles-resources.xml"; + private static final String TEST_PROFILE = "C:\\work\\org.hl7.fhir\\build\\publish\\namespace.profile.xml"; + private static final String PROFILES = "C:\\work\\org.hl7.fhir\\build\\publish\\profiles-resources.xml"; -igtodo - things to add: -- version -- list of resource names + igtodo - things to add: + - version + - list of resource names */ public class WorkerContext implements NameResolver { - private ITerminologyServices terminologyServices = new NullTerminologyServices(); + private ITerminologyServices terminologyServices = new NullTerminologyServices(); private IFHIRClient client = new NullClient(); private Map codeSystems = new HashMap(); private Map valueSets = new HashMap(); @@ -57,14 +58,13 @@ public class WorkerContext implements NameResolver { private Map extensionDefinitions = new HashMap(); private String version; private List resourceNames = new ArrayList(); - + private Map questionnaires = new HashMap(); public WorkerContext() { super(); } - public WorkerContext(ITerminologyServices conceptLocator, IFHIRClient client, Map codeSystems, - Map valueSets, Map maps, Map profiles) { + public WorkerContext(ITerminologyServices conceptLocator, IFHIRClient client, Map codeSystems, Map valueSets, Map maps, Map profiles) { super(); if (conceptLocator != null) this.terminologyServices = conceptLocator; @@ -85,8 +85,9 @@ public class WorkerContext implements NameResolver { } public boolean hasClient() { - return !(client == null || client instanceof NullClient); + return !(client == null || client instanceof NullClient); } + public IFHIRClient getClient() { return client; } @@ -111,8 +112,12 @@ public class WorkerContext implements NameResolver { return extensionDefinitions; } + public Map getQuestionnaires() { + return questionnaires; + } + public WorkerContext setTerminologyServices(ITerminologyServices terminologyServices) { - this.terminologyServices = terminologyServices; + this.terminologyServices = terminologyServices; return this; } @@ -124,181 +129,183 @@ public class WorkerContext implements NameResolver { return res; } - - - public void seeExtensionDefinition(String base, StructureDefinition ed) throws Exception { if (extensionDefinitions.get(ed.getUrl()) != null) - throw new Exception("duplicate extension definition: "+ed.getUrl()); + throw new Exception("duplicate extension definition: " + ed.getUrl()); extensionDefinitions.put(ed.getId(), ed); - extensionDefinitions.put(base+"/StructureDefinition/"+ed.getId(), ed); + extensionDefinitions.put(base + "/StructureDefinition/" + ed.getId(), ed); extensionDefinitions.put(ed.getUrl(), ed); } + public void seeQuestionnaire(String base, Questionnaire theQuestionnaire) throws Exception { + questionnaires.put(theQuestionnaire.getId(), theQuestionnaire); + questionnaires.put(base + "/Questionnaire/" + theQuestionnaire.getId(), theQuestionnaire); + } + public void seeValueSet(String base, ValueSet vs) { - valueSets.put(vs.getId(), vs); - valueSets.put(base+"/ValueSet/"+vs.getId(), vs); - valueSets.put(vs.getUrl(), vs); - if (vs.hasDefine()) { - codeSystems.put(vs.getDefine().getSystem().toString(), vs); - } - } + valueSets.put(vs.getId(), vs); + valueSets.put(base + "/ValueSet/" + vs.getId(), vs); + valueSets.put(vs.getUrl(), vs); + if (vs.hasDefine()) { + codeSystems.put(vs.getDefine().getSystem().toString(), vs); + } + } public void seeProfile(String base, StructureDefinition p) { - profiles.put(p.getId(), p); - profiles.put(base+"/StructureDefinition/"+p.getId(), p); - profiles.put(p.getUrl(), p); + profiles.put(p.getId(), p); + profiles.put(base + "/StructureDefinition/" + p.getId(), p); + profiles.put(p.getUrl(), p); } public class NullClient implements IFHIRClient { - @Override - public VersionInfo getVersions() { + @Override + public VersionInfo getVersions() { throw new Error("call to NullClient"); - } + } - @Override - public IFHIRClient initialize(String baseServiceUrl) throws URISyntaxException { + @Override + public IFHIRClient initialize(String baseServiceUrl) throws URISyntaxException { throw new Error("call to NullClient"); - } + } - @Override - public void initialize(String baseServiceUrl, int recordCount) throws URISyntaxException { + @Override + public void initialize(String baseServiceUrl, int recordCount) throws URISyntaxException { throw new Error("call to NullClient"); - } + } - @Override - public void setPreferredResourceFormat(ResourceFormat resourceFormat) { + @Override + public void setPreferredResourceFormat(ResourceFormat resourceFormat) { throw new Error("call to NullClient"); - } + } - @Override - public String getPreferredResourceFormat() { + @Override + public String getPreferredResourceFormat() { throw new Error("call to NullClient"); - } + } - @Override - public void setPreferredFeedFormat(FeedFormat feedFormat) { + @Override + public void setPreferredFeedFormat(FeedFormat feedFormat) { throw new Error("call to NullClient"); - } + } - @Override - public String getPreferredFeedFormat() { + @Override + public String getPreferredFeedFormat() { throw new Error("call to NullClient"); - } + } - @Override - public int getMaximumRecordCount() { + @Override + public int getMaximumRecordCount() { throw new Error("call to NullClient"); - } + } - @Override - public void setMaximumRecordCount(int recordCount) { + @Override + public void setMaximumRecordCount(int recordCount) { throw new Error("call to NullClient"); - } + } - @Override - public Conformance getConformanceStatement() { + @Override + public Conformance getConformanceStatement() { throw new Error("call to NullClient"); - } + } - @Override - public Conformance getConformanceStatement(boolean useOptionsVerb) { + @Override + public Conformance getConformanceStatement(boolean useOptionsVerb) { throw new Error("call to NullClient"); - } + } - @Override - public T read(Class resource, String id) { + @Override + public T read(Class resource, String id) { throw new Error("call to NullClient"); - } + } - @Override - public T vread(Class resource, String id, String versionid) { + @Override + public T vread(Class resource, String id, String versionid) { throw new Error("call to NullClient"); - } + } - @Override - public T update(Class resourceClass, T resource, String id) { + @Override + public T update(Class resourceClass, T resource, String id) { throw new Error("call to NullClient"); - } + } - @Override - public boolean delete(Class resourceClass, String id) { - throw new Error("call to NullClient"); - } - - @Override - public OperationOutcome create(Class resourceClass, T resource) { + @Override + public boolean delete(Class resourceClass, String id) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Calendar lastUpdate, Class resourceClass, String id) { + @Override + public OperationOutcome create(Class resourceClass, T resource) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Date lastUpdate, Class resourceClass, String id) { + @Override + public Bundle history(Calendar lastUpdate, Class resourceClass, String id) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Class resource, String id) { + @Override + public Bundle history(Date lastUpdate, Class resourceClass, String id) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Calendar lastUpdate, Class resourceClass) { + @Override + public Bundle history(Class resource, String id) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Date lastUpdate, Class resourceClass) { + @Override + public Bundle history(Calendar lastUpdate, Class resourceClass) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Class resourceClass) { + @Override + public Bundle history(Date lastUpdate, Class resourceClass) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Calendar lastUpdate) { + @Override + public Bundle history(Class resourceClass) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history(Date lastUpdate) { + @Override + public Bundle history(Calendar lastUpdate) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle history() { + @Override + public Bundle history(Date lastUpdate) { throw new Error("call to NullClient"); - } + } - @Override - public OperationOutcome validate(Class resourceClass, T resource, String id) { + @Override + public Bundle history() { throw new Error("call to NullClient"); - } + } - @Override - public Bundle search(Class resourceClass, Map params) { + @Override + public OperationOutcome validate(Class resourceClass, T resource, String id) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle searchPost(Class resourceClass, T resource, Map params) { + @Override + public Bundle search(Class resourceClass, Map params) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle transaction(Bundle batch) { + @Override + public Bundle searchPost(Class resourceClass, T resource, Map params) { throw new Error("call to NullClient"); - } + } - @Override - public Bundle fetchFeed(String url) { + @Override + public Bundle transaction(Bundle batch) { + throw new Error("call to NullClient"); + } + + @Override + public Bundle fetchFeed(String url) { throw new Error("call to NullClient"); } @@ -307,7 +314,7 @@ public class WorkerContext implements NameResolver { throw new Error("call to NullClient"); } - @Override + @Override public Parameters operateType(Class resourceClass, String name, Parameters params) { throw new Error("call to NullClient"); } @@ -325,20 +332,20 @@ public class WorkerContext implements NameResolver { } public StructureDefinition getExtensionStructure(StructureDefinition context, String url) throws Exception { - if (url.startsWith("#")) { + if (url.startsWith("#")) { throw new Error("Contained extensions not done yet"); - } else { - if (url.contains("#")) - url = url.substring(0, url.indexOf("#")); - StructureDefinition res = extensionDefinitions.get(url); - if (res == null) - res = profiles.get(url); - if (res == null) - return null; - if (res.getSnapshot() == null || res.getSnapshot().getElement().isEmpty()) - throw new Exception("no snapshot on extension for url "+url); - return res; - } + } else { + if (url.contains("#")) + url = url.substring(0, url.indexOf("#")); + StructureDefinition res = extensionDefinitions.get(url); + if (res == null) + res = profiles.get(url); + if (res == null) + return null; + if (res.getSnapshot() == null || res.getSnapshot().getElement().isEmpty()) + throw new Exception("no snapshot on extension for url " + url); + return res; + } } public class NullTerminologyServices implements ITerminologyServices { @@ -392,7 +399,7 @@ public class WorkerContext implements NameResolver { public boolean isResource(String name) { if (resourceNames.contains(name)) return true; - StructureDefinition sd = profiles.get("http://hl7.org/fhir/StructureDefinition/"+name); + StructureDefinition sd = profiles.get("http://hl7.org/fhir/StructureDefinition/" + name); return sd != null && (sd.getBase().endsWith("Resource") || sd.getBase().endsWith("DomainResource")); } @@ -408,5 +415,3 @@ public class WorkerContext implements NameResolver { } } - - \ No newline at end of file diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/BaseValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/BaseValidator.java index 256b9bf87af..fc51958a5d4 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/BaseValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/BaseValidator.java @@ -1,36 +1,38 @@ package org.hl7.fhir.instance.validation; /* -Copyright (c) 2011+, HL7, Inc -All rights reserved. + Copyright (c) 2011+, HL7, Inc + All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. + endorse or promote products derived from this software without specific + prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. -*/ + */ +import java.text.MessageFormat; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity; import org.hl7.fhir.instance.model.valuesets.IssueType; import org.hl7.fhir.instance.validation.ValidationMessage.Source; @@ -38,94 +40,243 @@ import org.hl7.fhir.instance.validation.ValidationMessage.Source; public class BaseValidator { protected Source source; - - protected boolean fail(List errors, IssueType type, int line, int col, String path, boolean b, String msg) { - if (!b) + + /** + * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean fail(List errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { + if (!thePass) { errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.FATAL)); - return b; + } + return thePass; } - - protected boolean rule(List errors, IssueType type, int line, int col, String path, boolean b, String msg) { - if (!b) - errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.ERROR)); - return b; - } - - protected boolean hint(List errors, IssueType type, int line, int col, String path, boolean b, String msg) { - if (!b) - errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION)); - return b; - } - - protected boolean warning(List errors, IssueType type, int line, int col, String path, boolean b, String msg) { - if (!b) - errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.WARNING)); - return b; - - } - - protected boolean fail(List errors, IssueType type, String path, boolean b, String msg) { - if (!b) + /** + * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean fail(List errors, IssueType type, List pathParts, boolean thePass, String msg) { + if (!thePass) { + String path = StringUtils.join(pathParts, '.'); errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL)); - return b; + } + return thePass; } - - protected boolean rule(List errors, IssueType type, String path, boolean b, String msg) { - if (!b) - errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); - return b; + /** + * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean fail(List errors, IssueType type, List pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { + if (!thePass) { + String path = StringUtils.join(pathParts, '.'); + errors.add(new ValidationMessage(source, type, -1, -1, path, formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL)); + } + return thePass; } - protected boolean rule(List errors, IssueType type, String path, boolean b, String msg, String html) { - if (!b) - errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.ERROR)); - return b; + /** + * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean fail(List errors, IssueType type, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL)); + } + return thePass; } - protected boolean hint(List errors, IssueType type, String path, boolean b, String msg) { - if (!b) - errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION)); - return b; - } - protected boolean warning(List errors, IssueType type, String path, boolean b, String msg) { - if (!b) - errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING)); - return b; - } - - protected boolean warning(List errors, IssueType type, String path, boolean b, String msg, String html) { - if (!b) - errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING)); - return b; + private String formatMessage(String theMessage, Object... theMessageArguments) { + String message; + if (theMessageArguments != null && theMessageArguments.length > 0) { + message = MessageFormat.format(theMessage, theMessageArguments); + } else { + message = theMessage; + } + return message; } protected boolean grammarWord(String w) { return w.equals("and") || w.equals("or") || w.equals("a") || w.equals("the") || w.equals("for") || w.equals("this") || w.equals("that") || w.equals("of"); } + /** + * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean hint(List errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean hint(List errors, IssueType type, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean rule(List errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.ERROR)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean rule(List errors, IssueType type, List pathParts, boolean thePass, String msg) { + if (!thePass) { + String path = StringUtils.join(pathParts, '.'); + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean rule(List errors, IssueType type, List pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { + if (!thePass) { + String path = StringUtils.join(pathParts, '.'); + String message = formatMessage(theMessage, theMessageArguments); + errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.ERROR)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean rule(List errors, IssueType type, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean rule(List errors, IssueType type, String path, boolean thePass, String msg, String html) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.ERROR)); + } + return thePass; + } + protected String splitByCamelCase(String s) { StringBuilder b = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); - if (Character.isUpperCase(c) && !(i == 0 || Character.isUpperCase(s.charAt(i-1)))) + if (Character.isUpperCase(c) && !(i == 0 || Character.isUpperCase(s.charAt(i - 1)))) b.append(' '); b.append(c); } return b.toString(); } - + protected String stripPunctuation(String s, boolean numbers) { StringBuilder b = new StringBuilder(); for (char c : s.toCharArray()) { int t = Character.getType(c); if (t == Character.UPPERCASE_LETTER || t == Character.LOWERCASE_LETTER || t == Character.TITLECASE_LETTER || t == Character.MODIFIER_LETTER || t == Character.OTHER_LETTER || (t == Character.DECIMAL_DIGIT_NUMBER && numbers) || (t == Character.LETTER_NUMBER && numbers) || c == ' ') b.append(c); - } + } return b.toString(); } + /** + * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean warning(List errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.WARNING)); + } + return thePass; + + } + + /** + * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean warning(List errors, IssueType type, String path, boolean thePass, String msg) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING)); + } + return thePass; + } + + /** + * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean warning(List errors, IssueType type, String path, boolean thePass, String msg, String html) { + if (!thePass) { + errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING)); + } + return thePass; + } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java index 1dcdba2a4df..075ab5d5530 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java @@ -842,7 +842,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // the instance validator had no issues against the base resource profile private void start(List errors, WrapperElement element, StructureDefinition profile, NodeStack stack) throws Exception { // profile is valid, and matches the resource name - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), profile.hasSnapshot(), "StructureDefinition has no snapshort - validation is against the snapshot, so it must be provided")) { + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), profile.hasSnapshot(), "StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided")) { validateElement(errors, profile, profile.getSnapshot().getElement().get(0), null, null, element, element.getName(), stack); checkDeclaredProfiles(errors, element, stack); @@ -878,7 +878,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (rule(errors, IssueType.INVALID, element.line(), element.col(), p, !Utilities.noString(ref), "StructureDefinition reference invalid")) { StructureDefinition pr = context.getProfiles().get(ref); if (warning(errors, IssueType.INVALID, element.line(), element.col(), p, pr != null, "StructureDefinition reference could not be resolved")) { - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), p, pr.hasSnapshot(), "StructureDefinition has no snapshort - validation is against the snapshot, so it must be provided")) { + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), p, pr.hasSnapshot(), "StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided")) { validateElement(errors, pr, pr.getSnapshot().getElement().get(0), null, null, element, element.getName(), stack); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/QuestionnaireAnswersValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/QuestionnaireAnswersValidator.java new file mode 100644 index 00000000000..e422dbfff8d --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/QuestionnaireAnswersValidator.java @@ -0,0 +1,389 @@ +package org.hl7.fhir.instance.validation; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.Attachment; +import org.hl7.fhir.instance.model.BooleanType; +import org.hl7.fhir.instance.model.Coding; +import org.hl7.fhir.instance.model.DateTimeType; +import org.hl7.fhir.instance.model.DateType; +import org.hl7.fhir.instance.model.DecimalType; +import org.hl7.fhir.instance.model.InstantType; +import org.hl7.fhir.instance.model.IntegerType; +import org.hl7.fhir.instance.model.Quantity; +import org.hl7.fhir.instance.model.Questionnaire; +import org.hl7.fhir.instance.model.Questionnaire.AnswerFormat; +import org.hl7.fhir.instance.model.Questionnaire.GroupComponent; +import org.hl7.fhir.instance.model.Questionnaire.QuestionComponent; +import org.hl7.fhir.instance.model.QuestionnaireAnswers; +import org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionAnswerComponent; +import org.hl7.fhir.instance.model.Reference; +import org.hl7.fhir.instance.model.Resource; +import org.hl7.fhir.instance.model.StringType; +import org.hl7.fhir.instance.model.TimeType; +import org.hl7.fhir.instance.model.Type; +import org.hl7.fhir.instance.model.UriType; +import org.hl7.fhir.instance.model.ValueSet; +import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent; +import org.hl7.fhir.instance.model.valuesets.IssueType; +import org.hl7.fhir.instance.utils.WorkerContext; + +/** + * Validates that an instance of {@link QuestionnaireAnswers} is valid against the {@link Questionnaire} that it claims to conform to. + * + * @author James Agnew + */ +public class QuestionnaireAnswersValidator extends BaseValidator { + + /* + * Note to anyone working on this class - + * + * This class has unit tests which run within the HAPI project build. Please sync any changes here to HAPI and ensure that unit tests are run. + */ + + private WorkerContext myWorkerCtx; + + public QuestionnaireAnswersValidator(WorkerContext theWorkerCtx) { + this.myWorkerCtx = theWorkerCtx; + } + + private Set> allowedTypes(Class theClass0) { + HashSet> retVal = new HashSet>(); + retVal.add(theClass0); + return Collections.unmodifiableSet(retVal); + } + + private List findAnswersByLinkId(List theQuestion, + String theLinkId) { + Validate.notBlank(theLinkId, "theLinkId must not be blank"); + + ArrayList retVal = new ArrayList(); + for (org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionComponent next : theQuestion) { + if (theLinkId.equals(next.getLinkId())) { + retVal.add(next); + } + } + return retVal; + } + + private List findGroupByLinkId(List theGroups, String theLinkId) { + Validate.notBlank(theLinkId, "theLinkId must not be blank"); + + ArrayList retVal = new ArrayList(); + for (org.hl7.fhir.instance.model.QuestionnaireAnswers.GroupComponent next : theGroups) { + if (theLinkId.equals(next.getLinkId())) { + retVal.add(next); + } + } + return retVal; + } + + public void validate(List theErrors, QuestionnaireAnswers theAnswers) { + LinkedList pathStack = new LinkedList(); + pathStack.add("QuestionnaireAnswers"); + pathStack.add(QuestionnaireAnswers.SP_QUESTIONNAIRE); + + if (!fail(theErrors, IssueType.INVALID, pathStack, theAnswers.hasQuestionnaire(), "QuestionnaireAnswers does not specity which questionnaire it is providing answers to")) { + return; + } + + Reference questionnaireRef = theAnswers.getQuestionnaire(); + Questionnaire questionnaire = getQuestionnaire(theAnswers, questionnaireRef); + if (!fail(theErrors, IssueType.INVALID, pathStack, questionnaire != null, "Questionnaire {0} is not found in the WorkerContext", theAnswers.getQuestionnaire().getReference())) { + return; + } + + pathStack.removeLast(); + pathStack.add("group(0)"); + validateGroup(theErrors, questionnaire.getGroup(), theAnswers.getGroup(), pathStack, theAnswers); + } + + private Questionnaire getQuestionnaire(QuestionnaireAnswers theAnswers, Reference theQuestionnaireRef) { + Questionnaire retVal; + if (theQuestionnaireRef.getReferenceElement().isLocal()) { + retVal = (Questionnaire) theQuestionnaireRef.getResource(); + if (retVal == null) { + for (Resource next : theAnswers.getContained()) { + if (theQuestionnaireRef.getReferenceElement().getValue().equals(next.getId())) { + retVal = (Questionnaire) next; + } + } + } + } else { + retVal = myWorkerCtx.getQuestionnaires().get(theQuestionnaireRef.getReferenceElement().getValue()); + } + return retVal; + } + + private ValueSet getValueSet(QuestionnaireAnswers theAnswers, Reference theQuestionnaireRef) { + ValueSet retVal; + if (theQuestionnaireRef.getReferenceElement().isLocal()) { + retVal = (ValueSet) theQuestionnaireRef.getResource(); + if (retVal == null) { + for (Resource next : theAnswers.getContained()) { + if (theQuestionnaireRef.getReferenceElement().getValue().equals(next.getId())) { + retVal = (ValueSet) next; + } + } + } + } else { + retVal = myWorkerCtx.getValueSets().get(theQuestionnaireRef.getReferenceElement().getValue()); + } + return retVal; + } + + private void validateGroup(List theErrors, GroupComponent theQuestGroup, org.hl7.fhir.instance.model.QuestionnaireAnswers.GroupComponent theAnsGroup, + LinkedList thePathStack, QuestionnaireAnswers theAnswers) { + + for (org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionComponent next : theAnsGroup.getQuestion()) { + rule(theErrors, IssueType.INVALID, thePathStack, isNotBlank(next.getLinkId()), "Question found with no linkId"); + } + + Set allowedQuestions = new HashSet(); + for (QuestionComponent nextQuestion : theQuestGroup.getQuestion()) { + allowedQuestions.add(nextQuestion.getLinkId()); + } + + for (int i = 0; i < theQuestGroup.getQuestion().size(); i++) { + QuestionComponent nextQuestion = theQuestGroup.getQuestion().get(i); + validateQuestion(theErrors, nextQuestion, theAnsGroup, thePathStack, theAnswers); + } + + // Check that there are no extra answers + for (int i = 0; i < theAnsGroup.getQuestion().size(); i++) { + org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionComponent nextQuestion = theAnsGroup.getQuestion().get(i); + thePathStack.add("question(" + i + ")"); + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, allowedQuestions.contains(nextQuestion.getLinkId()), "Found answer with linkId[{0}] but this ID is not allowed at this position", + nextQuestion.getLinkId()); + thePathStack.remove(); + } + + validateGroupGroups(theErrors, theQuestGroup, theAnsGroup, thePathStack, theAnswers); + + } + + private void validateQuestion(List theErrors, QuestionComponent theQuestion, org.hl7.fhir.instance.model.QuestionnaireAnswers.GroupComponent theAnsGroup, + LinkedList thePathStack, QuestionnaireAnswers theAnswers) { + String linkId = theQuestion.getLinkId(); + if (!fail(theErrors, IssueType.INVALID, thePathStack, isNotBlank(linkId), "Questionnaire is invalid, question found with no link ID")) { + return; + } + + AnswerFormat type = theQuestion.getType(); + if (type == null) { + if (theQuestion.getGroup().isEmpty()) { + rule(theErrors, IssueType.INVALID, thePathStack, false, "Questionnaire in invalid, no type and no groups specified for question with link ID[{0}]", linkId); + return; + } + type = AnswerFormat.NULL; + } + + List answers = findAnswersByLinkId(theAnsGroup.getQuestion(), linkId); + if (answers.size() > 1) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !theQuestion.getRequired(), "Multiple answers repetitions found with linkId[{0}]", linkId); + } + if (answers.size() == 0) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !theQuestion.getRequired(), "Missing answer to required question with linkId[{0}]", linkId); + return; + } + + org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionComponent answerQuestion = answers.get(0); + try { + thePathStack.add("question(" + answers.indexOf(answerQuestion) + ")"); + validateQuestionAnswers(theErrors, theQuestion, thePathStack, type, answerQuestion, theAnswers); + validateQuestionGroups(theErrors, theQuestion, answerQuestion, thePathStack, theAnswers); + } finally { + thePathStack.removeLast(); + } + } + + private void validateQuestionGroups(List theErrors, QuestionComponent theQuestion, org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionComponent theAnswerQuestion, + LinkedList thePathSpec, QuestionnaireAnswers theAnswers) { + validateGroups(theErrors, theQuestion.getGroup(), theAnswerQuestion.getGroup(), thePathSpec, theAnswers); + } + + private void validateGroupGroups(List theErrors, GroupComponent theQuestGroup, org.hl7.fhir.instance.model.QuestionnaireAnswers.GroupComponent theAnsGroup, + LinkedList thePathSpec, QuestionnaireAnswers theAnswers) { + validateGroups(theErrors, theQuestGroup.getGroup(), theAnsGroup.getGroup(), thePathSpec, theAnswers); + } + + private void validateGroups(List theErrors, List theQuestionGroups, List theAnswerGroups, + LinkedList thePathStack, QuestionnaireAnswers theAnswers) { + Set allowedGroups = new HashSet(); + for (GroupComponent nextQuestionGroup : theQuestionGroups) { + String linkId = nextQuestionGroup.getLinkId(); + allowedGroups.add(linkId); + + List answerGroups = findGroupByLinkId(theAnswerGroups, linkId); + if (answerGroups.isEmpty()) { + if (nextQuestionGroup.getRequired()) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required group with linkId[{0}]", linkId); + } + continue; + } + if (answerGroups.size() > 1) { + if (nextQuestionGroup.getRepeats() == false) { + int index = theAnswerGroups.indexOf(answerGroups.get(1)); + thePathStack.add("group(" + index + ")"); + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Multiple repetitions of group with linkId[{0}] found at this position, but this group can not repeat", linkId); + thePathStack.removeLast(); + } + } + for (org.hl7.fhir.instance.model.QuestionnaireAnswers.GroupComponent nextAnswerGroup : answerGroups) { + int index = theAnswerGroups.indexOf(answerGroups.get(1)); + thePathStack.add("group(" + index + ")"); + validateGroup(theErrors, nextQuestionGroup, nextAnswerGroup, thePathStack, theAnswers); + thePathStack.removeLast(); + } + } + + // Make sure there are no groups in answers that aren't in the questionnaire + int idx = -1; + for (org.hl7.fhir.instance.model.QuestionnaireAnswers.GroupComponent next : theAnswerGroups) { + idx++; + if (!allowedGroups.contains(next.getLinkId())) { + thePathStack.add("group(" + idx + ")"); + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Group with linkId[{0}] found at this position, but this group does not exist at this position in Questionnaire", next.getLinkId()); + thePathStack.removeLast(); + } + } + } + + private void validateQuestionAnswers(List theErrors, QuestionComponent theQuestion, LinkedList thePathStack, AnswerFormat type, + org.hl7.fhir.instance.model.QuestionnaireAnswers.QuestionComponent answerQuestion, QuestionnaireAnswers theAnswers) { + + String linkId = theQuestion.getLinkId(); + Set> allowedAnswerTypes = determineAllowedAnswerTypes(type); + if (allowedAnswerTypes.isEmpty()) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, answerQuestion.isEmpty(), "Question with linkId[{0}] has no answer type but an answer was provided", linkId); + } else { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !(answerQuestion.getAnswer().size() > 1 && !theQuestion.getRepeats()), "Multiple answers to non repeating question with linkId[{0}]", + linkId); + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !(theQuestion.getRequired() && answerQuestion.getAnswer().isEmpty()), "Missing answer to required question with linkId[{0}]", linkId); + } + + int answerIdx = -1; + for (QuestionAnswerComponent nextAnswer : answerQuestion.getAnswer()) { + answerIdx++; + try { + thePathStack.add("answer(" + answerIdx + ")"); + Type nextValue = nextAnswer.getValue(); + if (!allowedAnswerTypes.contains(nextValue.getClass())) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] found of type [{1}] but this is invalid for question of type [{2}]", linkId, nextValue + .getClass().getSimpleName(), type.toCode()); + continue; + } + + // Validate choice answers + if (type == AnswerFormat.CHOICE || type == AnswerFormat.OPENCHOICE) { + Coding coding = (Coding) nextAnswer.getValue(); + if (isBlank(coding.getCode()) && isBlank(coding.getSystem()) && isBlank(coding.getSystem())) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] is of type coding, but none of code, system, and display are populated", linkId); + continue; + } else if (isBlank(coding.getCode()) && isBlank(coding.getSystem())) { + if (type != AnswerFormat.OPENCHOICE) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, + "Answer to question with linkId[{0}] is of type only has a display populated (no code or system) but question does not allow {1}", linkId, AnswerFormat.OPENCHOICE.name()); + continue; + } + } else if (isBlank(coding.getCode()) || isBlank(coding.getSystem())) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, + "Answer to question with linkId[{0}] has a coding, but this coding does not contain a code and system (both must be present, or neither is the question allows {1})", linkId, + AnswerFormat.OPENCHOICE.name()); + continue; + } + + String optionsRef = theQuestion.getOptions().getReference(); + if (isNotBlank(optionsRef)) { + ValueSet valueSet = getValueSet(theAnswers, theQuestion.getOptions()); + if (valueSet == null) { + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Question with linkId[{0}] has options ValueSet[{1}] but this ValueSet can not be found", linkId, optionsRef); + continue; + } + + boolean found = false; + if (coding.getSystem().equals(valueSet.getDefine().getSystem())) { + for (ConceptDefinitionComponent next : valueSet.getDefine().getConcept()) { + if (coding.getCode().equals(next.getCode())) { + found = true; + break; + } + } + } + + rule(theErrors, IssueType.BUSINESSRULE, thePathStack, found, "Question with linkId[{0}] has answer with system[{1}] and code[{2}] but this is not a valid answer for ValueSet[{3}]", + linkId, coding.getSystem(), coding.getCode(), optionsRef); + } + } + + } finally { + thePathStack.removeLast(); + } + + } // for answers + } + + private Set> determineAllowedAnswerTypes(AnswerFormat type) { + Set> allowedAnswerTypes; + switch (type) { + case ATTACHMENT: + allowedAnswerTypes = allowedTypes(Attachment.class); + break; + case BOOLEAN: + allowedAnswerTypes = allowedTypes(BooleanType.class); + break; + case CHOICE: + allowedAnswerTypes = allowedTypes(Coding.class); + break; + case DATE: + allowedAnswerTypes = allowedTypes(DateType.class); + break; + case DATETIME: + allowedAnswerTypes = allowedTypes(DateTimeType.class); + break; + case DECIMAL: + allowedAnswerTypes = allowedTypes(DecimalType.class); + break; + case INSTANT: + allowedAnswerTypes = allowedTypes(InstantType.class); + break; + case INTEGER: + allowedAnswerTypes = allowedTypes(IntegerType.class); + break; + case OPENCHOICE: + allowedAnswerTypes = allowedTypes(Coding.class); + break; + case QUANTITY: + allowedAnswerTypes = allowedTypes(Quantity.class); + break; + case REFERENCE: + allowedAnswerTypes = allowedTypes(Reference.class); + break; + case STRING: + allowedAnswerTypes = allowedTypes(StringType.class); + break; + case TEXT: + allowedAnswerTypes = allowedTypes(StringType.class); + break; + case TIME: + allowedAnswerTypes = allowedTypes(TimeType.class); + break; + case URL: + allowedAnswerTypes = allowedTypes(UriType.class); + break; + case NULL: + default: + allowedAnswerTypes = Collections.emptySet(); + } + return allowedAnswerTypes; + } +} diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ValidationMessage.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ValidationMessage.java index ccba9fd0bb4..47a17f5cc65 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ValidationMessage.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ValidationMessage.java @@ -29,6 +29,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.CodeableConcept; import org.hl7.fhir.instance.model.OperationOutcome; import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity; @@ -183,6 +185,22 @@ public class ValidationMessage public String getHtml() { return html == null ? Utilities.escapeXml(message) : html; } + + /** + * Returns a representation of this ValidationMessage suitable for logging. The values of + * most of the internal fields are included, so this may not be suitable for display to + * an end user. + */ + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + b.append("level", level); + b.append("type", type); + b.append("location", location); + b.append("message", message); + return b.build(); + } + } \ No newline at end of file diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/InstantiationTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/InstantiationTest.java index 33c8d2eb402..b3ad9e837d5 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/InstantiationTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/InstantiationTest.java @@ -3,15 +3,18 @@ package ca.uhn.fhir.model; import static org.junit.Assert.*; import java.io.IOException; +import java.util.HashSet; import java.util.Map.Entry; import java.util.Properties; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.Test; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeChildResourceBlockDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; public class InstantiationTest { @@ -20,25 +23,36 @@ public class InstantiationTest { @Test public void testResources() throws IOException, ClassNotFoundException { FhirContext ctx = FhirContext.forDstu2Hl7Org(); - + Properties prop = new Properties(); prop.load(ctx.getVersion().getFhirVersionPropertiesFile()); - for(Entry next : prop.entrySet()) { + for (Entry next : prop.entrySet()) { if (next.getKey().toString().startsWith("resource.")) { Class clazz = (Class) Class.forName(next.getValue().toString()); RuntimeResourceDefinition res = ctx.getResourceDefinition(clazz); - - scanChildren(clazz, res); + + scanChildren(new HashSet>(), clazz, res); } } } - private void scanChildren(Class theClazz, BaseRuntimeElementCompositeDefinition theRes) { + private void scanChildren(HashSet> theHashSet, Class theClazz, BaseRuntimeElementCompositeDefinition theRes) { for (BaseRuntimeChildDefinition next : theRes.getChildren()) { if (next.getElementName().contains("_")) { fail("Element name " + next.getElementName() + " in type " + theClazz + " contains illegal '_'"); } + + if (next instanceof RuntimeChildResourceBlockDefinition) { + RuntimeChildResourceBlockDefinition nextBlock = (RuntimeChildResourceBlockDefinition) next; + for (String nextName : nextBlock.getValidChildNames()) { + BaseRuntimeElementCompositeDefinition elementDef = nextBlock.getChildByName(nextName); + if (theHashSet.add(elementDef.getImplementingClass())) { + scanChildren(theHashSet, elementDef.getImplementingClass(), elementDef); + } + } + } + } } - + } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/StructureDefinitionValidatorTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java similarity index 62% rename from hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/StructureDefinitionValidatorTest.java rename to hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java index bf358604d89..96b4d6c2378 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/StructureDefinitionValidatorTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java @@ -1,18 +1,18 @@ package ca.uhn.fhir.validation; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import java.util.List; -import org.hl7.fhir.instance.model.Patient; import org.hl7.fhir.instance.validation.ValidationMessage; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.server.EncodingEnum; -public class StructureDefinitionValidatorTest { +public class FhirInstanceValidatorTest { private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); @@ -23,8 +23,8 @@ public class StructureDefinitionValidatorTest { + "\"id\":\"123\"" + "}"; - StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx); - List output = val.validate(input, EncodingEnum.JSON, Patient.class); + FhirInstanceValidator val = new FhirInstanceValidator(); + List output = val.validate(ourCtx, input, EncodingEnum.JSON, "Patient"); assertEquals(output.toString(), 0, output.size()); } @@ -37,8 +37,8 @@ public class StructureDefinitionValidatorTest { + "}"; - StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx); - List output = val.validate(input, EncodingEnum.JSON, Patient.class); + FhirInstanceValidator val = new FhirInstanceValidator(); + List output = val.validate(ourCtx, input, EncodingEnum.JSON, "Patient"); assertEquals(output.toString(), 1, output.size()); assertThat(output.get(0).toXML(), stringContainsInOrder("/foo", "Element is unknown")); } @@ -49,8 +49,8 @@ public class StructureDefinitionValidatorTest { + "" + ""; - StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx); - List output = val.validate(input, EncodingEnum.XML, Patient.class); + FhirInstanceValidator val = new FhirInstanceValidator(); + List output = val.validate(ourCtx, input, EncodingEnum.XML, "Patient"); assertEquals(output.toString(), 0, output.size()); } @@ -62,8 +62,8 @@ public class StructureDefinitionValidatorTest { + "" + ""; - StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx); - List output = val.validate(input, EncodingEnum.XML, Patient.class); + FhirInstanceValidator val = new FhirInstanceValidator(); + List output = val.validate(ourCtx, input, EncodingEnum.XML, "Patient"); assertEquals(output.toString(), 1, output.size()); assertThat(output.get(0).toXML(), stringContainsInOrder("/f:Patient/f:foo", "Element is unknown")); } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/QuestionnaireAnswersValidatorTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/QuestionnaireAnswersValidatorTest.java new file mode 100644 index 00000000000..82c1ad88a68 --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/validation/QuestionnaireAnswersValidatorTest.java @@ -0,0 +1,185 @@ +package ca.uhn.fhir.validation; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.instance.model.Coding; +import org.hl7.fhir.instance.model.Questionnaire; +import org.hl7.fhir.instance.model.QuestionnaireAnswers; +import org.hl7.fhir.instance.model.Reference; +import org.hl7.fhir.instance.model.StringType; +import org.hl7.fhir.instance.model.ValueSet; +import org.hl7.fhir.instance.model.Questionnaire.AnswerFormat; +import org.hl7.fhir.instance.utils.WorkerContext; +import org.hl7.fhir.instance.validation.QuestionnaireAnswersValidator; +import org.hl7.fhir.instance.validation.ValidationMessage; +import org.junit.Before; +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; + +public class QuestionnaireAnswersValidatorTest { + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireAnswersValidatorTest.class); + private QuestionnaireAnswersValidator myVal; + + private WorkerContext myWorkerCtx; + + @Before + public void before() { + myWorkerCtx = new WorkerContext(); + myVal = new QuestionnaireAnswersValidator(myWorkerCtx); + } + + @Test + public void testAnswerWithWrongType() { + Questionnaire q = new Questionnaire(); + q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN); + + QuestionnaireAnswers qa = new QuestionnaireAnswers(); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); + + myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q); + List errors = new ArrayList(); + myVal.validate(errors, qa); + + ourLog.info(errors.toString()); + assertThat(errors.toString(), containsString("Answer to question with linkId[link0] found of type [StringType] but this is invalid for question of type [boolean]")); + } + + @Test + public void testCodedAnswer() { + String questionnaireRef = "http://example.com/Questionnaire/q1"; + + Questionnaire q = new Questionnaire(); + q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.CHOICE).setOptions(new Reference("http://somevalueset")); + myWorkerCtx.getQuestionnaires().put(questionnaireRef, q); + + ValueSet options = new ValueSet(); + options.getDefine().setSystem("urn:system").addConcept().setCode("code0"); + myWorkerCtx.getValueSets().put("http://somevalueset", options); + + QuestionnaireAnswers qa; + List errors; + + // Good code + + qa = new QuestionnaireAnswers(); + qa.getQuestionnaire().setReference(questionnaireRef); + qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code0")); + errors = new ArrayList(); + myVal.validate(errors, qa); + assertEquals(errors.toString(), 0, errors.size()); + + // Bad code + + qa = new QuestionnaireAnswers(); + qa.getQuestionnaire().setReference(questionnaireRef); + qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code1")); + errors = new ArrayList(); + myVal.validate(errors, qa); + ourLog.info(errors.toString()); + assertThat(errors.toString(), containsString("location=QuestionnaireAnswers.group(0).question(0).answer(0)")); + assertThat(errors.toString(), containsString("message=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset]")); + } + + + @Test + public void testMissingRequiredQuestion() { + + Questionnaire q = new Questionnaire(); + q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.STRING); + q.getGroup().addQuestion().setLinkId("link1").setRequired(true).setType(AnswerFormat.STRING); + + QuestionnaireAnswers qa = new QuestionnaireAnswers(); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getGroup().addQuestion().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); + + myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q); + List errors = new ArrayList(); + myVal.validate(errors, qa); + + ourLog.info(errors.toString()); + assertThat(errors.toString(), containsString("Missing answer to required question with linkId[link0]")); + } + + @Test + public void testUnexpectedAnswer() { + Questionnaire q = new Questionnaire(); + q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.BOOLEAN); + + QuestionnaireAnswers qa = new QuestionnaireAnswers(); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getGroup().addQuestion().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); + + myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q); + List errors = new ArrayList(); + myVal.validate(errors, qa); + + ourLog.info(errors.toString()); + assertThat(errors.toString(), containsString("location=QuestionnaireAnswers.group(0).question")); + assertThat(errors.toString(), containsString("message=Found answer with linkId[link1] but this ID is not allowed at this position")); + } + + @Test + public void testUnexpectedGroup() { + Questionnaire q = new Questionnaire(); + q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.BOOLEAN); + + QuestionnaireAnswers qa = new QuestionnaireAnswers(); + qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getGroup().addGroup().setLinkId("link1"); + + myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q); + List errors = new ArrayList(); + myVal.validate(errors, qa); + + ourLog.info(errors.toString()); + assertThat(errors.toString(), containsString("location=QuestionnaireAnswers.group(0).group(0)")); + assertThat(errors.toString(), containsString("Group with linkId[link1] found at this position, but this group does not exist at this position in Questionnaire")); + } + +// @Test + public void validateHealthConnexExample() throws Exception { + String input = IOUtils.toString(QuestionnaireAnswersValidatorTest.class.getResourceAsStream("/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml")); + + QuestionnaireAnswers qa = ourCtx.newXmlParser().parseResource(QuestionnaireAnswers.class, input); + ArrayList errors = new ArrayList(); + myVal.validate(errors, qa); + assertEquals(errors.toString(), 0, errors.size()); + + /* + * Now change a coded value + */ + //@formatter:off + input = input.replaceAll("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " ", "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " "); + assertThat(input, containsString("GGG")); + //@formatter:on + + qa = ourCtx.newXmlParser().parseResource(QuestionnaireAnswers.class, input); + errors = new ArrayList(); + myVal.validate(errors, qa); + assertEquals(errors.toString(), 10, errors.size()); + } + + +} diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/resources/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml b/hapi-fhir-structures-hl7org-dstu2/src/test/resources/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml new file mode 100644 index 00000000000..5ea64c3933c --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/resources/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml @@ -0,0 +1,4016 @@ + + + + + + + + +
!-- populated from the rendered HTML below -->
+
+ + + + + + + + + + + + + <group> + <linkId value="c9279048-37a1-415d-a935-b61efcff551c"/> + <title value="Introduction"/> + <repeats value="false"/> + <group> + <linkId value="f6cfe250-e710-4ba9-b18d-17c5e5834df9"/> + <title value="Introduction"/> + <repeats value="false"/> + <group> + <linkId value="da52dcdf-a097-4675-997d-24647bb677f6"/> + <title value="Please read to the consumer"/> + <repeats value="false"/> + </group> + <group> + <linkId value="b8f0c723-c66d-4502-83c1-913295c38dfd"/> + <title value="Question Notes"/> + <repeats value="false"/> + </group> + <group> + <linkId value="eee74f69-526b-4d1f-abb1-548c87f8c2bf"/> + <title value="Calculate the score"/> + <repeats value="false"/> + </group> + <group> + <linkId value="741fb913-0ea5-4152-9e09-8d84a52e371b"/> + <title value="Interpret the Score"/> + <repeats value="false"/> + </group> + </group> + </group> + <group> + <linkId value="84184fd55bf84379970a8c9bbf8bb80d"/> + <title value="Tobacco"/> + <repeats value="false"/> + <group> + <linkId value="ce01edf8-576a-45b7-81b8-81e8f25eb803"/> + <title value="Tobacco"/> + <repeats value="false"/> + <question> + <linkId value="70592294cd954586957016730afe50fc"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#7b4df9d4-476c-4af5-a1f3-78d664ec7541"/> + <display value="TobaccoQ1"/> + </options> + </question> + <question> + <linkId value="54cfa938b0a94a46ac748dd29879b669"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ2"/> + <display + value="Q2. In the past 3 months, how often have you used tobacco?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used tobacco?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#c3b93195-3959-4958-b5b1-408e62bccad2"/> + <display value="TobaccoQ2"/> + </options> + </question> + <question> + <linkId value="08453ec0ec69464684ff823feda8aaf4"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use tobacco?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use tobacco?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#adf39cde-6d81-4300-a393-0acc09913fb5"/> + <display value="TobaccoQ3"/> + </options> + </question> + <question> + <linkId value="be41841649bc48669c46054fde48f3e2"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ4"/> + <display + value="Q4. During the past three months how often has your use of tobacco led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of tobacco led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#3e01a1d0-b7d3-4afe-83c1-f97747f24124"/> + <display value="TobaccoQ4"/> + </options> + </question> + <question> + <linkId value="307bcc0d08054dd493dab1123a66d8b0"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of tobacco?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of tobacco?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#cf4e642f-a99e-4179-99ef-15d992de3cf3"/> + <display value="TobaccoQ5"/> + </options> + </question> + <question> + <linkId value="16a4581c67ac45d5aa8d37244cc5b80c"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of tobacco?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of tobacco?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#89b76c96-5dc7-4fe1-a339-e043a571e0fb"/> + <display value="TobaccoQ6"/> + </options> + </question> + <question> + <linkId value="d40f97cdd5154b10b27de9a824ad821a"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using tobacco?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using tobacco?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#f8973a4d-3812-4c00-a0ef-099083902442"/> + <display value="TobaccoQ7"/> + </options> + </question> + <question> + <linkId value="ed5422f57bab4fcb886a9c789047f0ec"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="TobaccoTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="e48a7dfd179b4516a30f25531b8bddd2"/> + <title value="Alcohol"/> + <repeats value="false"/> + <group> + <linkId value="e38b3251-96ad-4355-b1f2-5bef3e29357f"/> + <title value="Alcohol"/> + <repeats value="false"/> + <question> + <linkId value="42bbfde6faa24b4b825a4fc7ef99fdde"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#3762a808-e45d-411f-9a73-7c4b8d434382"/> + <display value="AlcoholQ1"/> + </options> + </question> + <question> + <linkId value="7ce60a28c5ad49388fa63a526540e95f"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ2"/> + <display + value="Q2. In the past 3 months, how often have you used alcohol?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used alcohol?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#074ecc51-5a33-4a45-926d-0dc9e5d3561b"/> + <display value="AlcoholQ2"/> + </options> + </question> + <question> + <linkId value="c59dc0c2e85a445d8a795e00d87e0451"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use alcohol?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use alcohol?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#fb428c18-df0b-4913-85c3-d7398315fd08"/> + <display value="AlcoholQ3"/> + </options> + </question> + <question> + <linkId value="07e4258068bb475e94f62e85d320deb1"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ4"/> + <display + value="Q4. During the past three months how often has your use of alcohol led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of alcohol led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#3bcfcca0-fe20-4807-be6f-66927249035b"/> + <display value="AlcoholQ4"/> + </options> + </question> + <question> + <linkId value="77b466edf74942e08b96b80cb8be81a1"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of alcohol?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of alcohol?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#7208efcd-b3a4-4526-a044-631669dd2a87"/> + <display value="AlcoholQ5"/> + </options> + </question> + <question> + <linkId value="eca35b534925440eb8e3e05db86da761"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of alcohol?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of alcohol?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#a257d9ae-ad52-47d4-88c5-6805a9ddcad3"/> + <display value="AlcoholQ6"/> + </options> + </question> + <question> + <linkId value="ada0c96f56384412827d892b2eedf9b2"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using alcohol?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using alcohol?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#4065921d-11a7-4c00-9bea-d576407ec6f2"/> + <display value="AlcoholQ7"/> + </options> + </question> + <question> + <linkId value="bd3a5e41a1794da3ab35061eda848f53"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AlcoholTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="a04b660f66a141f4a4f6fdefb7df3e1e"/> + <title value="Cannabis"/> + <repeats value="false"/> + <group> + <linkId value="76ee8f1f-48f6-420f-96a8-920a43a47071"/> + <title value="Cannabis"/> + <repeats value="false"/> + <question> + <linkId value="dc2ba5c68e6b4c608c509b995d5e6b9e"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#1eab2f67-f71c-4ae6-97e6-2489b62c73bd"/> + <display value="CannabisQ1"/> + </options> + </question> + <question> + <linkId value="1dabb8a4a3dd482aac7b562b34805075"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ2"/> + <display + value="Q2. In the past 3 months, how often have you used cannabis?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used cannabis?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#007f422d-3bbb-45b1-a9a8-395c92f4b731"/> + <display value="CannabisQ2"/> + </options> + </question> + <question> + <linkId value="19dd7f74da9449d98ab71ae6b995390c"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use cannabis?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use cannabis?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#d78e351f-71f0-401b-993f-62c10687e490"/> + <display value="CannabisQ3"/> + </options> + </question> + <question> + <linkId value="d3e8f546029f4808a8a286a5de266b44"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ4"/> + <display + value="Q4. During the past three months how often has your use of cannabis led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of cannabis led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#faaa7d8b-8084-4e27-814c-f74afa50c0b2"/> + <display value="CannabisQ4"/> + </options> + </question> + <question> + <linkId value="e57c5ac1bb9e45eb8bb91b15d59018f3"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of cannabis?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of cannabis?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#bbe9f6c7-ed54-4ce6-a4b1-e71c95fe8690"/> + <display value="CannabisQ5"/> + </options> + </question> + <question> + <linkId value="346c7343540347bcbd30dc1c1bba6a24"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of cannabis?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of cannabis?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#f259d2e0-9e81-41ac-9708-00e0f51c8ef4"/> + <display value="CannabisQ6"/> + </options> + </question> + <question> + <linkId value="48ff15bda2824ded9e7bd151dfeed008"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using cannabis?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using cannabis?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#e712fbee-dedd-480e-baca-d4b937ecbf94"/> + <display value="CannabisQ7"/> + </options> + </question> + <question> + <linkId value="23cd518ef46b4dc49e08fedb36f93c33"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CannabisTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="7db33f89e69f4f34a60090f5e93ef531"/> + <title value="Cocaine"/> + <repeats value="false"/> + <group> + <linkId value="66b1cc70-1311-4393-9a02-3be08cff5fe9"/> + <title value="Cocaine"/> + <repeats value="false"/> + <question> + <linkId value="2c986c7a635048b1a38be8fb9bfde557"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#8a117478-234e-481a-b5a9-600877cca30d"/> + <display value="CocaineQ1"/> + </options> + </question> + <question> + <linkId value="cfd29e64b4e840ffa61c7b14a251fe58"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ2"/> + <display + value="Q2. In the past 3 months, how often have you used cocaine?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used cocaine?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#69db607d-04d9-425c-b1a8-e5e0c0e27126"/> + <display value="CocaineQ2"/> + </options> + </question> + <question> + <linkId value="5cdbbbae7795418493af86e2c973a896"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use cocaine?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use cocaine?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#74429e80-a649-4a0c-aace-f54945a0c167"/> + <display value="CocaineQ3"/> + </options> + </question> + <question> + <linkId value="dddfebc6d85a4a60a4e3796674c8f6f3"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ4"/> + <display + value="Q4. During the past three months how often has your use of cocaine led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of cocaine led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#353e3f40-f38f-4935-8952-3f374ea64344"/> + <display value="CocaineQ4"/> + </options> + </question> + <question> + <linkId value="8dbd16c733f04ea4867f03aa32590764"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of cocaine?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of cocaine?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#0ebccb3d-c591-4422-8e9b-62b8b4519234"/> + <display value="CocaineQ5"/> + </options> + </question> + <question> + <linkId value="3886f4a942714795acb7e832391c164a"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of cocaine?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of cocaine?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#b9016401-f982-4882-8ca9-10a26243e87f"/> + <display value="CocaineQ6"/> + </options> + </question> + <question> + <linkId value="bc8fbc34e0d54e93a65104c2a5528cd9"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using cocaine?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using cocaine?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#9fad4280-2178-4665-80c3-fee2dbb9f6aa"/> + <display value="CocaineQ7"/> + </options> + </question> + <question> + <linkId value="3f05efbead6f4972b7fb9e79f87abe35"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="CocaineTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="9195dcd8e37e40359a54af86f8865f5a"/> + <title value="Amphetamine Type Stimulants"/> + <repeats value="false"/> + <group> + <linkId value="d250d3f2-9758-48dc-820f-2f3fb8302f20"/> + <title value="Amphetamine Type Stimulants"/> + <repeats value="false"/> + <question> + <linkId value="a3f3fb3689c845dd8c344694c155b00b"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#ca244a57-8fb3-4923-8411-d29ed829a962"/> + <display value="AmphetaminesQ1"/> + </options> + </question> + <question> + <linkId value="5354869f1c374043bd86e98fa1cd0a98"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ2"/> + <display + value="Q2. In the past 3 months, how often have you used amphetamine type stimulants?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used amphetamine type stimulants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#a19f461a-4436-4de3-8b3d-94261f4a570d"/> + <display value="AmphetaminesQ2"/> + </options> + </question> + <question> + <linkId value="7d8adc8f5e654d248f397165dad77a0d"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use amphetamine type stimulants?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use amphetamine type stimulants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#9e4d4a7c-2a49-4c21-84e6-8545632d7d1c"/> + <display value="AmphetaminesQ3"/> + </options> + </question> + <question> + <linkId value="5642831307ed4a8b82b3f58f1e371220"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ4"/> + <display + value="Q4. During the past three months how often has your use of amphetamine type stimulants led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of amphetamine type stimulants led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#9ce80491-5f80-4c22-bf3f-014fab48700a"/> + <display value="AmphetaminesQ4"/> + </options> + </question> + <question> + <linkId value="4ee565c148f84108bfbd5bf7105aa7cc"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of amphetamine type stimulants?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of amphetamine type stimulants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#0d8eb84b-e7fd-4110-b5f0-040b3fb4dd6e"/> + <display value="AmphetaminesQ5"/> + </options> + </question> + <question> + <linkId value="ef97f28841014d40a7fb4f8197117b6b"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of amphetamine type stimulants?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of amphetamine type stimulants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#11f02771-14a5-4233-bfb5-d9b498991564"/> + <display value="AmphetaminesQ6"/> + </options> + </question> + <question> + <linkId value="80ea29d4cc45446eb300296857dc396b"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using amphetamine type stimulants?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using amphetamine type stimulants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#b69a368a-7e96-48fd-9cab-1f9878b30be2"/> + <display value="AmphetaminesQ7"/> + </options> + </question> + <question> + <linkId value="cccc02e5bbe54845b23299f3c95fc1c2"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="AmphetaminesTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="95f1f5cd59004f9d83e076cde747d12b"/> + <title value="Inhalants"/> + <repeats value="false"/> + <group> + <linkId value="56e2f55f-faa5-43ac-b458-50a76447d8dc"/> + <title value="Inhalants"/> + <repeats value="false"/> + <question> + <linkId value="994c81b82b6d409e9fc6a8a85f7ecb90"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#12613d67-bad0-4270-985f-3b2d48b0ca04"/> + <display value="InhalantsQ1"/> + </options> + </question> + <question> + <linkId value="6bf2faf8b82f44e1b0ae2cf7fbbb3d63"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ2"/> + <display + value="Q2. In the past 3 months, how often have you used inhalants?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used inhalants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#c82183f1-00fe-4aef-866f-07a61c32cc1d"/> + <display value="InhalantsQ2"/> + </options> + </question> + <question> + <linkId value="c74e5e6eceb04812aa6a31ba29e9f126"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use inhalants?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use inhalants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#e45a6868-6386-4ae5-aa62-fe39c361f17f"/> + <display value="InhalantsQ3"/> + </options> + </question> + <question> + <linkId value="2321bf325f8744ac90c3a326da9497d8"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ4"/> + <display + value="Q4. During the past three months how often has your use of inhalants led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of inhalants led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#e66c7dc1-a745-4f37-832b-dba88a5e1780"/> + <display value="InhalantsQ4"/> + </options> + </question> + <question> + <linkId value="8c18cfb82cf14d0887f047236e856c96"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of inhalants?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of inhalants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#9b31d8e3-2748-4e5e-82d2-08da8a796027"/> + <display value="InhalantsQ5"/> + </options> + </question> + <question> + <linkId value="3e9f5f00b6354b3888ebae307500009f"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of inhalants?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of inhalants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#84fdd89a-f7f1-4145-b81d-b60e09d102f2"/> + <display value="InhalantsQ6"/> + </options> + </question> + <question> + <linkId value="99e4afb881db45eea59d7505899b7157"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using inhalants?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using inhalants?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#a614c0c7-6dd5-4c34-8625-c99f1be1a5ca"/> + <display value="InhalantsQ7"/> + </options> + </question> + <question> + <linkId value="028933e0b3a24e2190f45cb99fc5bba9"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="InhalantsTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="90fff6138a0d4306be2a68dd6007b611"/> + <title value="Sedatives"/> + <repeats value="false"/> + <group> + <linkId value="d40c35cc-b1b8-439d-aeaa-59f0b12572bd"/> + <title value="Sedatives"/> + <repeats value="false"/> + <question> + <linkId value="8afc770daf1d4642b555157998f79f22"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#55a014f0-765c-47cf-ba8a-a635f88a52d5"/> + <display value="SedativesQ1"/> + </options> + </question> + <question> + <linkId value="4a4b888f726748988bb94c1effc83371"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ2"/> + <display + value="Q2. In the past 3 months, how often have you used sedatives?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used sedatives?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#418141d8-7ee1-4f0c-9d87-695c9f8799a1"/> + <display value="SedativesQ2"/> + </options> + </question> + <question> + <linkId value="cda61a51d6734c5a96f75ac6236e2b0a"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use sedatives?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use sedatives?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#3dc39a1e-93ef-43e9-8351-a0c748da8e92"/> + <display value="SedativesQ3"/> + </options> + </question> + <question> + <linkId value="1588295d0e7541ccb5180f4c492b8c9b"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ4"/> + <display + value="Q4. During the past three months how often has your use of sedatives led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of sedatives led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#52e2899b-6658-413e-8c16-597ad12cf401"/> + <display value="SedativesQ4"/> + </options> + </question> + <question> + <linkId value="b36b59fd3ffc46cabcd789d7a470ef13"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of sedatives?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of sedatives?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#5be2b4c6-bc0b-4c01-8c65-9f39c9ddf694"/> + <display value="SedativesQ5"/> + </options> + </question> + <question> + <linkId value="f116474b8ad34877918e1d6edd3b3c79"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of sedatives?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of sedatives?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#c7fe56d0-4072-4608-a6a4-594fc973e562"/> + <display value="SedativesQ6"/> + </options> + </question> + <question> + <linkId value="86e52ef9e7484160a96cdbc5b3802147"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using sedatives?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using sedatives?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#18f07f5b-9f3c-4e6e-b8ba-5f2af1627586"/> + <display value="SedativesQ7"/> + </options> + </question> + <question> + <linkId value="fa6b1971987e415984481004b4da3f42"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="SedativesTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="ced7dfe498334120b9b9c9da22e8ca7c"/> + <title value="Hallucinogens"/> + <repeats value="false"/> + <group> + <linkId value="8ee1af26-c077-4c22-b112-513419ce5bdb"/> + <title value="Hallucinogens"/> + <repeats value="false"/> + <question> + <linkId value="fa07cdb553af40048f65574dd18c6efe"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#ec8878b7-a979-4fa0-a781-3a47c2271758"/> + <display value="HallucinogensQ1"/> + </options> + </question> + <question> + <linkId value="121e74ffe63d4561aa16bf055d1a02ec"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ2"/> + <display + value="Q2. In the past 3 months, how often have you used hallucinogens?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used hallucinogens?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#ccced9e7-8fb9-480c-857c-2e7906435ee7"/> + <display value="HallucinogensQ2"/> + </options> + </question> + <question> + <linkId value="7efad285afb34eac96ee29cc99181452"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use hallucinogens?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use hallucinogens?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#52a7c902-827f-4f4f-9d4f-202f2b48ab0e"/> + <display value="HallucinogensQ3"/> + </options> + </question> + <question> + <linkId value="b45937c3e3554bdfadfaf5070a2630bd"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ4"/> + <display + value="Q4. During the past three months how often has your use of hallucinogens led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of hallucinogens led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#0bbff40f-486b-4269-945a-a83f64d44b7e"/> + <display value="HallucinogensQ4"/> + </options> + </question> + <question> + <linkId value="e4a5cc2bdf3349928fcd62bd7d65c870"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of hallucinogens?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of hallucinogens?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#a700f478-829a-4067-95f0-0833b59e16ae"/> + <display value="HallucinogensQ5"/> + </options> + </question> + <question> + <linkId value="2cd18832d3e047b2a317d33acb96528c"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of hallucinogens?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of hallucinogens?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#b3dfde43-49e1-466f-b3eb-f138449b5785"/> + <display value="HallucinogensQ6"/> + </options> + </question> + <question> + <linkId value="d494016c7cb146a2a6b97f744dbbaa62"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using hallucinogens?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using hallucinogens?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#691881b9-08a7-4b61-91ec-dff54974ebce"/> + <display value="HallucinogensQ7"/> + </options> + </question> + <question> + <linkId value="c782f2f775a443d8be0d096424600b4e"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="HallucinogensTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="6acc3c592f814c6f8b79b1c371adab25"/> + <title value="Opioids"/> + <repeats value="false"/> + <group> + <linkId value="54a67da7-9e45-44a4-aa77-317130e6718b"/> + <title value="Opioids"/> + <repeats value="false"/> + <question> + <linkId value="2fa9235d2e334b97bd3bbe347680f49c"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#4ed757e8-1fae-4970-b3cc-ce0492470056"/> + <display value="OpioidsQ1"/> + </options> + </question> + <question> + <linkId value="ff7f1e03e63e4007ae70515c965fe4bf"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ2"/> + <display + value="Q2. In the past 3 months, how often have you used opioids?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used opioids?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#c555bd4a-a9ec-4003-8a96-d7be3d287f10"/> + <display value="OpioidsQ2"/> + </options> + </question> + <question> + <linkId value="239d36a34817481ab8477ac4d8203f33"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use opioids?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use opioids?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#cf605f4b-a781-4228-bcff-a33c37f33b16"/> + <display value="OpioidsQ3"/> + </options> + </question> + <question> + <linkId value="097fe1bdb95048cf812e7170979f3da5"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ4"/> + <display + value="Q4. During the past three months how often has your use of opioids led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of opioids led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#584b475b-81a3-4eda-b610-0670f014b1a4"/> + <display value="OpioidsQ4"/> + </options> + </question> + <question> + <linkId value="553a1bab94e54162b30d9daa2f339b8f"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of opioids?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of opioids?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#88737ff3-db69-468d-8523-c572c6431161"/> + <display value="OpioidsQ5"/> + </options> + </question> + <question> + <linkId value="02b9a82a1c94450082ca083f5a556b3f"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of opioids?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of opioids?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#62d11f9b-f290-4be8-bd07-50a060c2d886"/> + <display value="OpioidsQ6"/> + </options> + </question> + <question> + <linkId value="bb81b53dc6f74d0482c7265f7c399056"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using opioids?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using opioids?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#a5ef609e-a711-440c-a25b-4f5796be81d6"/> + <display value="OpioidsQ7"/> + </options> + </question> + <question> + <linkId value="ad998e6772ed4d259b546150404b55dd"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OpioidsTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="a7f67207fb10424b84e899158d0dd1a4"/> + <title value="Other"/> + <repeats value="false"/> + <group> + <linkId value="93cc6e52-fce7-4846-a6c9-1167d5b2add9"/> + <title value="Other Substances"/> + <repeats value="false"/> + <question> + <linkId value="28fb2011120f4864aa139bccf7d3eb55"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ1"/> + <display + value="Q1. In your life which of the following substances have you ever used?"/> + </concept> + <text + value="Q1. In your life which of the following substances have you ever used?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#c2a6e2d5-90a1-40bc-8407-2b26e134c942"/> + <display value="OtherQ1"/> + </options> + </question> + <question> + <linkId value="5e2780016583471f868a5cca5357af60"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ2"/> + <display + value="Q2. In the past 3 months, how often have you used other substances?"/> + </concept> + <text + value="Q2. In the past 3 months, how often have you used other substances?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#9bdcd39e-ad1e-4957-a362-3e7b152e6201"/> + <display value="OtherQ2"/> + </options> + </question> + <question> + <linkId value="e6a6dca687d04945856ff98833e44a05"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ3"/> + <display + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use other substances?"/> + </concept> + <text + value="Q3. During the past 3 months, how often have you had a strong desire or urge to use other substances?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#a03b9d57-8786-4216-b34e-123def44dcd8"/> + <display value="OtherQ3"/> + </options> + </question> + <question> + <linkId value="19d4aef92a9441c69e399618cea34f0f"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ4"/> + <display + value="Q4. During the past three months how often has your use of other substances led to health, social, legal or financial problems?"/> + </concept> + <text + value="Q4. During the past three months how often has your use of other substances led to health, social, legal or financial problems?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#d9695f22-6581-4610-b22c-b0eee8b3e53e"/> + <display value="OtherQ4"/> + </options> + </question> + <question> + <linkId value="0b32a64afe0745959b4a58f2b89ab450"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ5"/> + <display + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of other substances?"/> + </concept> + <text + value="Q5. During the past 3 months how often have you failed to do what was normally expected of you because of your use of other substances?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#66972a5b-563c-4724-96c5-f48afe1ff7b7"/> + <display value="OtherQ5"/> + </options> + </question> + <question> + <linkId value="1d9a3c1ab2a246e5b9365946f1348675"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ6"/> + <display + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of other substances?"/> + </concept> + <text + value="Q6. Has a friend or relative or anyone else ever expressed concern about your use of other substances?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#9973a791-bc86-4db4-9ead-1f4c12e89d18"/> + <display value="OtherQ6"/> + </options> + </question> + <question> + <linkId value="8176fb1bc8324009b80fe5efe4257b5e"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherQ7"/> + <display + value="Q7. Have you ever tried and failed to control, cut down or stop using other substances?"/> + </concept> + <text + value="Q7. Have you ever tried and failed to control, cut down or stop using other substances?"/> + <type value="choice"/> + <repeats value="false"/> + <options> + <reference value="#d7b1cb79-ddc7-4b33-a124-c2d87d6bb2cd"/> + <display value="OtherQ7"/> + </options> + </question> + <question> + <linkId value="57768329c5ea410a8a96945a10239195"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="OtherTotal"/> + <display value="Total"/> + </concept> + <text value="Total"/> + <type value="decimal"/> + </question> + </group> + </group> + <group> + <linkId value="9d75a9d22b884327ba0d4aacc2ccaf38"/> + <title value="Injecting, Risk"/> + <repeats value="false"/> + <group> + <linkId value="58f3056a-5421-4149-a6d1-8f15b130c1af"/> + <title value="Injecting"/> + <repeats value="false"/> + <question> + <linkId value="510b860735af4c899ca1097e3adb8725"/> + <concept> + <code value="InjectingQ8"/> + </concept> + <text + value="Q8. Have you ever used any drug by injection (non-medical use)?"/> + <type value="boolean"/> + </question> + </group> + <group> + <linkId value="a4d3d385-bbe1-42a7-983f-984dc96768e4"/> + <title value="Risk"/> + <repeats value="false"/> + <question> + <linkId value="3ca235d36b914d1984d31fc45026dba4"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="Risk"/> + <display value="Risk"/> + </concept> + <text value="Risk"/> + <type value="string"/> + </question> + <question> + <linkId value="985c083e8ebc45d591927ab4ad6f19c1"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="Treatment"/> + <display value="Treatment"/> + </concept> + <text value="Treatment"/> + <type value="string"/> + </question> + <question> + <linkId value="7a2bf2d8dba740c8bf970711f202a9a5"/> + <concept> + <system value="http://tcm7.com.au/fhir/7-12/Questionnaire"/> + <code value="Referral"/> + <display value="Referral"/> + </concept> + <text value="Referral"/> + <type value="string"/> + </question> + </group> + </group> + </group> + </Questionnaire> + </contained> + <contained> + <ValueSet> + <id value="7b4df9d4-476c-4af5-a1f3-78d664ec7541"/> + <name value="TobaccoQ1"/> + <define> + <system value="bd2280ae-e821-476b-bed7-ea8aba2985aa"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="c3b93195-3959-4958-b5b1-408e62bccad2"/> + <name value="TobaccoQ2"/> + <define> + <system value="f69573b8-cb63-4d31-85a4-23ac784735ab"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="adf39cde-6d81-4300-a393-0acc09913fb5"/> + <name value="TobaccoQ3"/> + <define> + <system value="c03f9219-7c7f-4e43-9295-d0a8248271db"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="3e01a1d0-b7d3-4afe-83c1-f97747f24124"/> + <name value="TobaccoQ4"/> + <define> + <system value="954af1ba-7ebb-46ea-af66-d5622125f889"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="cf4e642f-a99e-4179-99ef-15d992de3cf3"/> + <name value="TobaccoQ5"/> + <define> + <system value="14ca652f-80f3-4120-9457-036a1e29add2"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="89b76c96-5dc7-4fe1-a339-e043a571e0fb"/> + <name value="TobaccoQ6"/> + <define> + <system value="0e3651e0-c085-4269-8730-106db014d8aa"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="f8973a4d-3812-4c00-a0ef-099083902442"/> + <name value="TobaccoQ7"/> + <define> + <system value="4754caf8-ac02-43cc-aa17-1a3c33fe8ad4"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="3762a808-e45d-411f-9a73-7c4b8d434382"/> + <name value="AlcoholQ1"/> + <define> + <system value="01e4179a-5aa4-4aa0-9a9f-1dec4573850b"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="074ecc51-5a33-4a45-926d-0dc9e5d3561b"/> + <name value="AlcoholQ2"/> + <define> + <system value="a4345107-c7b3-4723-aa56-d2e03153656e"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="fb428c18-df0b-4913-85c3-d7398315fd08"/> + <name value="AlcoholQ3"/> + <define> + <system value="7c170f23-b202-4b04-95d0-5790238961ba"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="3bcfcca0-fe20-4807-be6f-66927249035b"/> + <name value="AlcoholQ4"/> + <define> + <system value="4d3707a6-1edd-40f5-881f-d67ba5101bce"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="7208efcd-b3a4-4526-a044-631669dd2a87"/> + <name value="AlcoholQ5"/> + <define> + <system value="0d7e6ad1-8deb-4ca4-bf83-668bbef32504"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="a257d9ae-ad52-47d4-88c5-6805a9ddcad3"/> + <name value="AlcoholQ6"/> + <define> + <system value="c046f15f-6959-47eb-96f3-062678242c7b"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="4065921d-11a7-4c00-9bea-d576407ec6f2"/> + <name value="AlcoholQ7"/> + <define> + <system value="1f8f572c-4e64-4903-9f44-69732f6a8591"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="1eab2f67-f71c-4ae6-97e6-2489b62c73bd"/> + <name value="CannabisQ1"/> + <define> + <system value="cb20d465-6570-4359-b4a0-070361f5f6c2"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="007f422d-3bbb-45b1-a9a8-395c92f4b731"/> + <name value="CannabisQ2"/> + <define> + <system value="66fcf510-e198-4d94-a720-38451a40a521"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="d78e351f-71f0-401b-993f-62c10687e490"/> + <name value="CannabisQ3"/> + <define> + <system value="af556602-a5c4-4aa1-ae55-81c5104643a1"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="faaa7d8b-8084-4e27-814c-f74afa50c0b2"/> + <name value="CannabisQ4"/> + <define> + <system value="6b1f374e-dea6-4303-8217-82f090765f00"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="bbe9f6c7-ed54-4ce6-a4b1-e71c95fe8690"/> + <name value="CannabisQ5"/> + <define> + <system value="13ed880b-f0c9-4416-bb7b-192a761ea94e"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="f259d2e0-9e81-41ac-9708-00e0f51c8ef4"/> + <name value="CannabisQ6"/> + <define> + <system value="893ad941-6398-4a8e-8263-a8cc73a408fc"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="e712fbee-dedd-480e-baca-d4b937ecbf94"/> + <name value="CannabisQ7"/> + <define> + <system value="4a21dd4a-6e9d-4b01-a114-ada2aa213ca7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="8a117478-234e-481a-b5a9-600877cca30d"/> + <name value="CocaineQ1"/> + <define> + <system value="558fa07d-156d-4aa0-8612-f39bcd467faa"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="69db607d-04d9-425c-b1a8-e5e0c0e27126"/> + <name value="CocaineQ2"/> + <define> + <system value="c38e470a-a418-4341-89d9-e810d63c5216"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="74429e80-a649-4a0c-aace-f54945a0c167"/> + <name value="CocaineQ3"/> + <define> + <system value="62bc22bf-f366-4e35-86d7-47e9a3f39132"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="353e3f40-f38f-4935-8952-3f374ea64344"/> + <name value="CocaineQ4"/> + <define> + <system value="0f5171f1-bec3-4473-8b77-6f3f5bf951a8"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="0ebccb3d-c591-4422-8e9b-62b8b4519234"/> + <name value="CocaineQ5"/> + <define> + <system value="5e8ff8df-a200-4cf3-a961-836089faef4f"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="b9016401-f982-4882-8ca9-10a26243e87f"/> + <name value="CocaineQ6"/> + <define> + <system value="7da3903d-905d-4a85-9156-9f02cf9d47a7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="9fad4280-2178-4665-80c3-fee2dbb9f6aa"/> + <name value="CocaineQ7"/> + <define> + <system value="bebb3d4f-7422-4a1f-95f9-56a6f3c5d550"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="ca244a57-8fb3-4923-8411-d29ed829a962"/> + <name value="AmphetaminesQ1"/> + <define> + <system value="0e5fe632-1d5f-4785-8d25-eb74afe7872c"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="a19f461a-4436-4de3-8b3d-94261f4a570d"/> + <name value="AmphetaminesQ2"/> + <define> + <system value="51d4b3de-62fe-4eb2-8e12-1d64d9ce6f5f"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="9e4d4a7c-2a49-4c21-84e6-8545632d7d1c"/> + <name value="AmphetaminesQ3"/> + <define> + <system value="6e9f170c-75bb-4a4e-8ebc-8032475e2c12"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="9ce80491-5f80-4c22-bf3f-014fab48700a"/> + <name value="AmphetaminesQ4"/> + <define> + <system value="b1db0ed0-b306-41f9-a312-33a5911506e7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="0d8eb84b-e7fd-4110-b5f0-040b3fb4dd6e"/> + <name value="AmphetaminesQ5"/> + <define> + <system value="f3c2568b-03e6-4c93-a3a3-5f848ef62c73"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="11f02771-14a5-4233-bfb5-d9b498991564"/> + <name value="AmphetaminesQ6"/> + <define> + <system value="896a817d-fd50-448e-b983-7cbbaa9f4131"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="b69a368a-7e96-48fd-9cab-1f9878b30be2"/> + <name value="AmphetaminesQ7"/> + <define> + <system value="e339f26b-eb20-48d2-88e6-d57455600c9f"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="12613d67-bad0-4270-985f-3b2d48b0ca04"/> + <name value="InhalantsQ1"/> + <define> + <system value="e8844934-295e-402d-b6b4-554125e19382"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="c82183f1-00fe-4aef-866f-07a61c32cc1d"/> + <name value="InhalantsQ2"/> + <define> + <system value="f4cec4a2-5044-47ef-b479-a9bb66357827"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="e45a6868-6386-4ae5-aa62-fe39c361f17f"/> + <name value="InhalantsQ3"/> + <define> + <system value="6ae65f77-749f-4f13-b7c8-3e0329189fd7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="e66c7dc1-a745-4f37-832b-dba88a5e1780"/> + <name value="InhalantsQ4"/> + <define> + <system value="98b1a3d5-ad98-4b00-8ee4-7e3ab7642b06"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="9b31d8e3-2748-4e5e-82d2-08da8a796027"/> + <name value="InhalantsQ5"/> + <define> + <system value="f6813321-b840-4bd3-9639-fc665e260fce"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="84fdd89a-f7f1-4145-b81d-b60e09d102f2"/> + <name value="InhalantsQ6"/> + <define> + <system value="58232edd-c90a-47aa-a992-95e3ef93d4ab"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="a614c0c7-6dd5-4c34-8625-c99f1be1a5ca"/> + <name value="InhalantsQ7"/> + <define> + <system value="afc0846c-e09f-46ec-8aed-ff1561ff86f7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="55a014f0-765c-47cf-ba8a-a635f88a52d5"/> + <name value="SedativesQ1"/> + <define> + <system value="a04ab410-599a-454d-9496-59b04b5fb46a"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="418141d8-7ee1-4f0c-9d87-695c9f8799a1"/> + <name value="SedativesQ2"/> + <define> + <system value="0d1b7113-b7a2-41ac-92f4-0155812e42b7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="3dc39a1e-93ef-43e9-8351-a0c748da8e92"/> + <name value="SedativesQ3"/> + <define> + <system value="57902b6c-21e0-4d60-b2a6-7ddfe04dbc11"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="52e2899b-6658-413e-8c16-597ad12cf401"/> + <name value="SedativesQ4"/> + <define> + <system value="86c5ec06-b86f-416c-b313-7aa2accb4114"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="5be2b4c6-bc0b-4c01-8c65-9f39c9ddf694"/> + <name value="SedativesQ5"/> + <define> + <system value="7d5a48ae-9f9d-44be-945a-5552e49f5573"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="c7fe56d0-4072-4608-a6a4-594fc973e562"/> + <name value="SedativesQ6"/> + <define> + <system value="bace0a19-4408-424b-9721-c73bac25af1c"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="18f07f5b-9f3c-4e6e-b8ba-5f2af1627586"/> + <name value="SedativesQ7"/> + <define> + <system value="640a346c-6778-468e-a820-3e1dd389caed"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="ec8878b7-a979-4fa0-a781-3a47c2271758"/> + <name value="HallucinogensQ1"/> + <define> + <system value="bd1b5b2d-82d0-4cb1-b590-a94e15a02463"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="ccced9e7-8fb9-480c-857c-2e7906435ee7"/> + <name value="HallucinogensQ2"/> + <define> + <system value="874c93b1-0da4-47eb-9034-48d2a690fee7"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="52a7c902-827f-4f4f-9d4f-202f2b48ab0e"/> + <name value="HallucinogensQ3"/> + <define> + <system value="19b29955-dce8-4198-b98f-946868139386"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="0bbff40f-486b-4269-945a-a83f64d44b7e"/> + <name value="HallucinogensQ4"/> + <define> + <system value="e8d2b9c3-b6f4-4029-b124-450e726f50ff"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="a700f478-829a-4067-95f0-0833b59e16ae"/> + <name value="HallucinogensQ5"/> + <define> + <system value="445e64ed-a70c-4416-b1db-e75edf466aa3"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="b3dfde43-49e1-466f-b3eb-f138449b5785"/> + <name value="HallucinogensQ6"/> + <define> + <system value="33ea5e88-a5b4-45e1-a491-3e9c0ecd8c32"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="691881b9-08a7-4b61-91ec-dff54974ebce"/> + <name value="HallucinogensQ7"/> + <define> + <system value="e8adb26a-c63d-49b2-92e0-0071a378dd75"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="4ed757e8-1fae-4970-b3cc-ce0492470056"/> + <name value="OpioidsQ1"/> + <define> + <system value="569d8066-d9a3-44a2-bebb-27108d6ab892"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="c555bd4a-a9ec-4003-8a96-d7be3d287f10"/> + <name value="OpioidsQ2"/> + <define> + <system value="05c178ea-d25f-4e11-8a77-dede83d08f36"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="cf605f4b-a781-4228-bcff-a33c37f33b16"/> + <name value="OpioidsQ3"/> + <define> + <system value="700487f6-f1e3-4699-a078-43f9cd2ea091"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="584b475b-81a3-4eda-b610-0670f014b1a4"/> + <name value="OpioidsQ4"/> + <define> + <system value="c0c3fef0-a39a-40da-ae25-825f4fcc4594"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="88737ff3-db69-468d-8523-c572c6431161"/> + <name value="OpioidsQ5"/> + <define> + <system value="c41944c0-6bd8-4ef0-bd96-88f2eb62aad8"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="62d11f9b-f290-4be8-bd07-50a060c2d886"/> + <name value="OpioidsQ6"/> + <define> + <system value="1ae96004-12e7-442f-b334-2850ecb7b36c"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="a5ef609e-a711-440c-a25b-4f5796be81d6"/> + <name value="OpioidsQ7"/> + <define> + <system value="74afa419-e3f9-4b3b-9191-87f2ca5d14c2"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="c2a6e2d5-90a1-40bc-8407-2b26e134c942"/> + <name value="OtherQ1"/> + <define> + <system value="7cb1e7d6-c9ee-4243-80c3-d0ec70d9fd2a"/> + <concept> + <code value="Y"/> + <abstract value="false"/> + <display value="Yes"/> + </concept> + <concept> + <code value="N"/> + <abstract value="false"/> + <display value="No"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="9bdcd39e-ad1e-4957-a362-3e7b152e6201"/> + <name value="OtherQ2"/> + <define> + <system value="c7433954-5b72-410e-83e9-5367ac0dbe6b"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="2"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="a03b9d57-8786-4216-b34e-123def44dcd8"/> + <name value="OtherQ3"/> + <define> + <system value="0ec6c5f0-a037-4d07-aa4d-ba69658c92e9"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="d9695f22-6581-4610-b22c-b0eee8b3e53e"/> + <name value="OtherQ4"/> + <define> + <system value="f583fa95-6c87-44f8-a796-02bbb618741e"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="4"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="66972a5b-563c-4724-96c5-f48afe1ff7b7"/> + <name value="OtherQ5"/> + <define> + <system value="f398014d-f605-4a87-b5e9-29cf5d5b5de1"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="5"/> + <abstract value="false"/> + <display value="Once/twice"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Monthly"/> + </concept> + <concept> + <code value="7"/> + <abstract value="false"/> + <display value="Weekly"/> + </concept> + <concept> + <code value="8"/> + <abstract value="false"/> + <display value="Daily/Almost Daily"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="9973a791-bc86-4db4-9ead-1f4c12e89d18"/> + <name value="OtherQ6"/> + <define> + <system value="5d59f981-01cc-4873-88dd-742c958bad60"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <contained> + <ValueSet> + <id value="d7b1cb79-ddc7-4b33-a124-c2d87d6bb2cd"/> + <name value="OtherQ7"/> + <define> + <system value="cc5abb42-1a07-4301-80dd-2796353c639a"/> + <concept> + <code value="0"/> + <abstract value="false"/> + <display value="Never"/> + </concept> + <concept> + <code value="6"/> + <abstract value="false"/> + <display value="Yes, in past 3 months"/> + </concept> + <concept> + <code value="3"/> + <abstract value="false"/> + <display value="Yes, not in past 3 months"/> + </concept> + </define> + </ValueSet> + </contained> + <questionnaire> + <reference value="#4dad1b31b7764d0581fd5b654cbfb5be"/> + </questionnaire> + <status value="completed"/> + <authored value="2015-07-10T00:45:12.260Z"/> + <group> + <group> + <linkId value="f6cfe250-e710-4ba9-b18d-17c5e5834df9"/> + <group> + <linkId value="da52dcdf-a097-4675-997d-24647bb677f6"/> + </group> + <group> + <linkId value="b8f0c723-c66d-4502-83c1-913295c38dfd"/> + </group> + <group> + <linkId value="eee74f69-526b-4d1f-abb1-548c87f8c2bf"/> + </group> + <group> + <linkId value="741fb913-0ea5-4152-9e09-8d84a52e371b"/> + </group> + </group> + <group> + <linkId value="ce01edf8-576a-45b7-81b8-81e8f25eb803"/> + <question> + <linkId value="70592294cd954586957016730afe50fc"/> + <text value=""/> + <answer> + <valueCoding> + <system value="bd2280ae-e821-476b-bed7-ea8aba2985aa"/> + <code value="Y"/> + <display value="Yes"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="54cfa938b0a94a46ac748dd29879b669"/> + <text value=""/> + <answer> + <valueCoding> + <system value="f69573b8-cb63-4d31-85a4-23ac784735ab"/> + <code value="2"/> + <display value="Once/twice"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="08453ec0ec69464684ff823feda8aaf4"/> + <text value=""/> + <answer> + <valueCoding> + <system value="c03f9219-7c7f-4e43-9295-d0a8248271db"/> + <code value="3"/> + <display value="Once/twice"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="be41841649bc48669c46054fde48f3e2"/> + <text value=""/> + <answer> + <valueCoding> + <system value="954af1ba-7ebb-46ea-af66-d5622125f889"/> + <code value="0"/> + <display value="Never"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="307bcc0d08054dd493dab1123a66d8b0"/> + <text value=""/> + <answer> + <valueCoding> + <system value="14ca652f-80f3-4120-9457-036a1e29add2"/> + <code value="5"/> + <display value="Once/twice"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="16a4581c67ac45d5aa8d37244cc5b80c"/> + <text value=""/> + <answer> + <valueCoding> + <system value="0e3651e0-c085-4269-8730-106db014d8aa"/> + <code value="0"/> + <display value="Never"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="d40f97cdd5154b10b27de9a824ad821a"/> + <text value=""/> + <answer> + <valueCoding> + <system value="4754caf8-ac02-43cc-aa17-1a3c33fe8ad4"/> + <code value="0"/> + <display value="Never"/> + </valueCoding> + </answer> + </question> + <question> + <linkId value="ed5422f57bab4fcb886a9c789047f0ec"/> + <text value=""/> + <answer> + <valueString value="5"/> + </answer> + </question> + </group> + <group> + <linkId value="e38b3251-96ad-4355-b1f2-5bef3e29357f"/> + <question> + <linkId value="42bbfde6faa24b4b825a4fc7ef99fdde"/> + <text value=""/> + </question> + <question> + <linkId value="7ce60a28c5ad49388fa63a526540e95f"/> + <text value=""/> + </question> + <question> + <linkId value="c59dc0c2e85a445d8a795e00d87e0451"/> + <text value=""/> + </question> + <question> + <linkId value="07e4258068bb475e94f62e85d320deb1"/> + <text value=""/> + </question> + <question> + <linkId value="77b466edf74942e08b96b80cb8be81a1"/> + <text value=""/> + </question> + <question> + <linkId value="eca35b534925440eb8e3e05db86da761"/> + <text value=""/> + </question> + <question> + <linkId value="ada0c96f56384412827d892b2eedf9b2"/> + <text value=""/> + </question> + <question> + <linkId value="bd3a5e41a1794da3ab35061eda848f53"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="76ee8f1f-48f6-420f-96a8-920a43a47071"/> + <question> + <linkId value="dc2ba5c68e6b4c608c509b995d5e6b9e"/> + <text value=""/> + </question> + <question> + <linkId value="1dabb8a4a3dd482aac7b562b34805075"/> + <text value=""/> + </question> + <question> + <linkId value="19dd7f74da9449d98ab71ae6b995390c"/> + <text value=""/> + </question> + <question> + <linkId value="d3e8f546029f4808a8a286a5de266b44"/> + <text value=""/> + </question> + <question> + <linkId value="e57c5ac1bb9e45eb8bb91b15d59018f3"/> + <text value=""/> + </question> + <question> + <linkId value="346c7343540347bcbd30dc1c1bba6a24"/> + <text value=""/> + </question> + <question> + <linkId value="48ff15bda2824ded9e7bd151dfeed008"/> + <text value=""/> + </question> + <question> + <linkId value="23cd518ef46b4dc49e08fedb36f93c33"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="66b1cc70-1311-4393-9a02-3be08cff5fe9"/> + <question> + <linkId value="2c986c7a635048b1a38be8fb9bfde557"/> + <text value=""/> + </question> + <question> + <linkId value="cfd29e64b4e840ffa61c7b14a251fe58"/> + <text value=""/> + </question> + <question> + <linkId value="5cdbbbae7795418493af86e2c973a896"/> + <text value=""/> + </question> + <question> + <linkId value="dddfebc6d85a4a60a4e3796674c8f6f3"/> + <text value=""/> + </question> + <question> + <linkId value="8dbd16c733f04ea4867f03aa32590764"/> + <text value=""/> + </question> + <question> + <linkId value="3886f4a942714795acb7e832391c164a"/> + <text value=""/> + </question> + <question> + <linkId value="bc8fbc34e0d54e93a65104c2a5528cd9"/> + <text value=""/> + </question> + <question> + <linkId value="3f05efbead6f4972b7fb9e79f87abe35"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="d250d3f2-9758-48dc-820f-2f3fb8302f20"/> + <question> + <linkId value="a3f3fb3689c845dd8c344694c155b00b"/> + <text value=""/> + </question> + <question> + <linkId value="5354869f1c374043bd86e98fa1cd0a98"/> + <text value=""/> + </question> + <question> + <linkId value="7d8adc8f5e654d248f397165dad77a0d"/> + <text value=""/> + </question> + <question> + <linkId value="5642831307ed4a8b82b3f58f1e371220"/> + <text value=""/> + </question> + <question> + <linkId value="4ee565c148f84108bfbd5bf7105aa7cc"/> + <text value=""/> + </question> + <question> + <linkId value="ef97f28841014d40a7fb4f8197117b6b"/> + <text value=""/> + </question> + <question> + <linkId value="80ea29d4cc45446eb300296857dc396b"/> + <text value=""/> + </question> + <question> + <linkId value="cccc02e5bbe54845b23299f3c95fc1c2"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="56e2f55f-faa5-43ac-b458-50a76447d8dc"/> + <question> + <linkId value="994c81b82b6d409e9fc6a8a85f7ecb90"/> + <text value=""/> + </question> + <question> + <linkId value="6bf2faf8b82f44e1b0ae2cf7fbbb3d63"/> + <text value=""/> + </question> + <question> + <linkId value="c74e5e6eceb04812aa6a31ba29e9f126"/> + <text value=""/> + </question> + <question> + <linkId value="2321bf325f8744ac90c3a326da9497d8"/> + <text value=""/> + </question> + <question> + <linkId value="8c18cfb82cf14d0887f047236e856c96"/> + <text value=""/> + </question> + <question> + <linkId value="3e9f5f00b6354b3888ebae307500009f"/> + <text value=""/> + </question> + <question> + <linkId value="99e4afb881db45eea59d7505899b7157"/> + <text value=""/> + </question> + <question> + <linkId value="028933e0b3a24e2190f45cb99fc5bba9"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="d40c35cc-b1b8-439d-aeaa-59f0b12572bd"/> + <question> + <linkId value="8afc770daf1d4642b555157998f79f22"/> + <text value=""/> + </question> + <question> + <linkId value="4a4b888f726748988bb94c1effc83371"/> + <text value=""/> + </question> + <question> + <linkId value="cda61a51d6734c5a96f75ac6236e2b0a"/> + <text value=""/> + </question> + <question> + <linkId value="1588295d0e7541ccb5180f4c492b8c9b"/> + <text value=""/> + </question> + <question> + <linkId value="b36b59fd3ffc46cabcd789d7a470ef13"/> + <text value=""/> + </question> + <question> + <linkId value="f116474b8ad34877918e1d6edd3b3c79"/> + <text value=""/> + </question> + <question> + <linkId value="86e52ef9e7484160a96cdbc5b3802147"/> + <text value=""/> + </question> + <question> + <linkId value="fa6b1971987e415984481004b4da3f42"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="8ee1af26-c077-4c22-b112-513419ce5bdb"/> + <question> + <linkId value="fa07cdb553af40048f65574dd18c6efe"/> + <text value=""/> + </question> + <question> + <linkId value="121e74ffe63d4561aa16bf055d1a02ec"/> + <text value=""/> + </question> + <question> + <linkId value="7efad285afb34eac96ee29cc99181452"/> + <text value=""/> + </question> + <question> + <linkId value="b45937c3e3554bdfadfaf5070a2630bd"/> + <text value=""/> + </question> + <question> + <linkId value="e4a5cc2bdf3349928fcd62bd7d65c870"/> + <text value=""/> + </question> + <question> + <linkId value="2cd18832d3e047b2a317d33acb96528c"/> + <text value=""/> + </question> + <question> + <linkId value="d494016c7cb146a2a6b97f744dbbaa62"/> + <text value=""/> + </question> + <question> + <linkId value="c782f2f775a443d8be0d096424600b4e"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="54a67da7-9e45-44a4-aa77-317130e6718b"/> + <question> + <linkId value="2fa9235d2e334b97bd3bbe347680f49c"/> + <text value=""/> + </question> + <question> + <linkId value="ff7f1e03e63e4007ae70515c965fe4bf"/> + <text value=""/> + </question> + <question> + <linkId value="239d36a34817481ab8477ac4d8203f33"/> + <text value=""/> + </question> + <question> + <linkId value="097fe1bdb95048cf812e7170979f3da5"/> + <text value=""/> + </question> + <question> + <linkId value="553a1bab94e54162b30d9daa2f339b8f"/> + <text value=""/> + </question> + <question> + <linkId value="02b9a82a1c94450082ca083f5a556b3f"/> + <text value=""/> + </question> + <question> + <linkId value="bb81b53dc6f74d0482c7265f7c399056"/> + <text value=""/> + </question> + <question> + <linkId value="ad998e6772ed4d259b546150404b55dd"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="93cc6e52-fce7-4846-a6c9-1167d5b2add9"/> + <question> + <linkId value="28fb2011120f4864aa139bccf7d3eb55"/> + <text value=""/> + </question> + <question> + <linkId value="5e2780016583471f868a5cca5357af60"/> + <text value=""/> + </question> + <question> + <linkId value="e6a6dca687d04945856ff98833e44a05"/> + <text value=""/> + </question> + <question> + <linkId value="19d4aef92a9441c69e399618cea34f0f"/> + <text value=""/> + </question> + <question> + <linkId value="0b32a64afe0745959b4a58f2b89ab450"/> + <text value=""/> + </question> + <question> + <linkId value="1d9a3c1ab2a246e5b9365946f1348675"/> + <text value=""/> + </question> + <question> + <linkId value="8176fb1bc8324009b80fe5efe4257b5e"/> + <text value=""/> + </question> + <question> + <linkId value="57768329c5ea410a8a96945a10239195"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + <group> + <linkId value="58f3056a-5421-4149-a6d1-8f15b130c1af"/> + <question> + <linkId value="510b860735af4c899ca1097e3adb8725"/> + <text value=""/> + <answer> + <valueBoolean value="false"/> + </answer> + </question> + </group> + <group> + <linkId value="a4d3d385-bbe1-42a7-983f-984dc96768e4"/> + <question> + <linkId value="3ca235d36b914d1984d31fc45026dba4"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + <question> + <linkId value="985c083e8ebc45d591927ab4ad6f19c1"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + <question> + <linkId value="7a2bf2d8dba740c8bf970711f202a9a5"/> + <text value=""/> + <answer> + <valueString value=""/> + </answer> + </question> + </group> + </group> +</QuestionnaireAnswers> diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index d59d79908b2..c5ae48d90cb 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -5,7 +5,11 @@ import java.util.*; import org.apache.commons.lang3.StringUtils; +#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers')) +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider${className}Dstu2; +#else import ca.uhn.fhir.jpa.provider.JpaResourceProvider${versionCapitalized}; +#end import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; @@ -20,8 +24,10 @@ import ca.uhn.fhir.model.dstu.resource.Binary; // import ca.uhn.fhir.model.api.Bundle; public class ${className}ResourceProvider extends -#if ( $version != 'dstu' && (${className} == 'Patient' || ${className} == 'Encounter') ) - BaseJpaResourceProvider${className}${versionCapitalized}<${className}> +## We have specialized base classes for RPs that handle certain resource types. These +## RPs implement type specific operations +#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers')) + BaseJpaResourceProvider${className}${versionCapitalized} #else JpaResourceProvider${versionCapitalized}<${className}> #end diff --git a/sync_ri.sh b/sync_ri.sh index 741c4191f85..002b4b6e7ac 100755 --- a/sync_ri.sh +++ b/sync_ri.sh @@ -12,7 +12,6 @@ cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.instance/src/org/hl7/f cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.instance/src/org/hl7/fhir/instance/utils/NameResolver.java hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/utils/ cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.instance/src/org/hl7/fhir/instance/validation/BaseValidator.java hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.instance/src/org/hl7/fhir/instance/validation/InstanceValidator.java hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ -cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.instance/src/org/hl7/fhir/instance/validation/ProfileValidator.java hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.instance/src/org/hl7/fhir/instance/validation/ValidationMessage.java hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/ValidationMessage.java cp -vp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.utilities/src/org/hl7/fhir/utilities/xhtml/HeirarchicalTableGenerator.java hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/utilities/xhtml/