diff --git a/examples/src/main/java/example/RestfulPatientResourceProviderMore.java b/examples/src/main/java/example/RestfulPatientResourceProviderMore.java index 42070c2cd78..3254fb1faec 100644 --- a/examples/src/main/java/example/RestfulPatientResourceProviderMore.java +++ b/examples/src/main/java/example/RestfulPatientResourceProviderMore.java @@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.annotation.Count; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.DeleteTags; +import ca.uhn.fhir.rest.annotation.Elements; import ca.uhn.fhir.rest.annotation.GetTags; import ca.uhn.fhir.rest.annotation.History; import ca.uhn.fhir.rest.annotation.IdParam; @@ -59,6 +60,7 @@ import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IRestfulClient; @@ -102,6 +104,17 @@ public List getAllOrganizations() { } //END SNIPPET: searchAll + +//START SNIPPET: summaryAndElements +@Search +public List search( + SummaryEnum theSummary, // will receive the summary (no annotation required) + @Elements Set theElements // (requires the @Elements annotation) + ) { + return null; // todo: populate +} +//END SNIPPET: summaryAndElements + //START SNIPPET: searchCompartment public class PatientRp implements IResourceProvider { @@ -705,7 +718,7 @@ public MethodOutcome createPatient(@ResourceParam Patient thePatient) { // You can also add an OperationOutcome resource to return // This part is optional though: OperationOutcome outcome = new OperationOutcome(); - outcome.addIssue().setDetails("One minor issue detected"); + outcome.addIssue().setDiagnostics("One minor issue detected"); retVal.setOperationOutcome(outcome); return retVal; @@ -841,7 +854,7 @@ public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient t // You can also add an OperationOutcome resource to return // This part is optional though: OperationOutcome outcome = new OperationOutcome(); - outcome.addIssue().setDetails("One minor issue detected"); + outcome.addIssue().setDiagnostics("One minor issue detected"); retVal.setOperationOutcome(outcome); // If your server supports creating resources during an update if they don't already exist @@ -881,7 +894,7 @@ public MethodOutcome validatePatient(@ResourceParam Patient thePatient, // You may also add an OperationOutcome resource to return // This part is optional though: OperationOutcome outcome = new OperationOutcome(); - outcome.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("One minor issue detected"); + outcome.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDiagnostics("One minor issue detected"); retVal.setOperationOutcome(outcome); return retVal; @@ -1127,7 +1140,7 @@ public List transaction(@TransactionParam List theResource // If wanted, you may optionally also return an OperationOutcome resource // If present, the OperationOutcome must come first in the returned list. OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Completed successfully"); + oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Completed successfully"); retVal.add(0, oo); return retVal; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 4614fff9e15..9eb24c1239a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -27,6 +27,7 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -68,6 +69,7 @@ import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; public abstract class BaseParser implements IParser { @@ -133,6 +135,10 @@ public abstract class BaseParser implements IParser { myNext = null; } else if (myNext.getDef().getElementName().equals("id")) { myNext = null; + } else if (!myNext.shouldBeEncoded()) { + myNext = null; + } else if (isSummaryMode() && !myNext.getDef().isSummary()) { + myNext = null; } else if (myNext.getDef() instanceof RuntimeChildNarrativeDefinition) { if (isSuppressNarratives() || isSummaryMode()) { myNext = null; @@ -143,10 +149,6 @@ public abstract class BaseParser implements IParser { if (theContainedResource) { myNext = null; } - } else if (isSummaryMode() && !myNext.getDef().isSummary()) { - myNext = null; - } else if (!myNext.shouldBeEncoded()) { - myNext = null; } } while (myNext == null); @@ -533,7 +535,11 @@ public abstract class BaseParser implements IParser { IBaseMetaType metaValue; if (theValues != null && theValues.size() >= 1) { metaValue = (IBaseMetaType) theValues.iterator().next(); - metaValue = metaValue.copy(); + try { + metaValue = (IBaseMetaType) metaValue.getClass().getMethod("copy").invoke(metaValue); + } catch (Exception e) { + throw new InternalErrorException("Failed to duplicate meta", e); + } } else { metaValue = (IBaseMetaType) metaChildUncast1.newInstance(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Elements.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Elements.java new file mode 100644 index 00000000000..1d7c638c64a --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Elements.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.rest.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * On a method which returns resource(s), a parameter of type + * Set<String> with this annotation will be passed the + * contents of the _elements parameter + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Elements { + // just a marker +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java index 1eda49b8487..1213364839d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java @@ -246,6 +246,16 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding elements = ElementsParameter.getElementsValueOrNull(theRequest); + if (elements != null && summaryMode != null && !summaryMode.equals(Collections.singleton(SummaryEnum.FALSE))) { + throw new InvalidRequestException("Cannot combine the " + Constants.PARAM_SUMMARY + " and " + Constants.PARAM_ELEMENTS + " parameters"); + } + Set elementsAppliesTo = null; + if (elements != null && isNotBlank(myResourceName)) { + elementsAppliesTo = Collections.singleton(myResourceName); + } + // Is this request coming from a browser String uaHeader = theRequest.getServletRequest().getHeader("user-agent"); boolean requestIsBrowser = false; @@ -332,7 +342,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding includes = getRequestIncludesFromParams(params); @@ -367,7 +377,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding theSummaryMode) { - if (theSummaryMode == null || !theSummaryMode.contains(SummaryEnum.COUNT)) { - return false; - } - return true; - } - /** * Should the response include a Content-Location header. Search method bunding (and any others?) may override this to disable the content-location, since it doesn't make sense */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java new file mode 100644 index 00000000000..62768163752 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java @@ -0,0 +1,128 @@ +package ca.uhn.fhir.rest.method; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * 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 static org.apache.commons.lang3.StringUtils.*; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.param.CollectionBinder; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; + +public class ElementsParameter implements IParameter { + + @SuppressWarnings("rawtypes") + private Class myInnerCollectionType; + + @SuppressWarnings("unchecked") + @Override + public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException { + if (theSourceClientArgument instanceof Collection) { + StringBuilder values = new StringBuilder(); + for (String next : (Collection) theSourceClientArgument) { + if (isNotBlank(next)) { + if (values.length() > 0) { + values.append(','); + } + values.append(next); + } + } + theTargetQueryArguments.put(Constants.PARAM_ELEMENTS, Collections.singletonList(values.toString())); + } else { + String elements = (String) theSourceClientArgument; + if (elements != null) { + theTargetQueryArguments.put(Constants.PARAM_ELEMENTS, Collections.singletonList(elements)); + } + } + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { + Set value = getElementsValueOrNull(theRequest); + if (value == null || value.isEmpty()) { + return null; + } + + if (myInnerCollectionType == null) { + return StringUtils.join(value, ','); + } + + try { + Collection retVal = myInnerCollectionType.newInstance(); + retVal.addAll(value); + return retVal; + } catch (InstantiationException e) { + throw new InternalErrorException("Failed to instantiate " + myInnerCollectionType, e); + } catch (IllegalAccessException e) { + throw new InternalErrorException("Failed to instantiate " + myInnerCollectionType, e); + } + } + + public static Set getElementsValueOrNull(RequestDetails theRequest) { + String[] summary = theRequest.getParameters().get(Constants.PARAM_ELEMENTS); + + if (summary != null && summary.length > 0) { + Set retVal = new HashSet(); + for (String next : summary) { + StringTokenizer tok = new StringTokenizer(next, ","); + while (tok.hasMoreTokens()) { + String token = tok.nextToken(); + if (isNotBlank(token)) { + retVal.add(token); + } + } + } + if (retVal.isEmpty()) { + return null; + } + return retVal; + } else { + return null; + } + } + + @Override + public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { + if (theOuterCollectionType != null) { + throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is of type " + SummaryEnum.class + " but can not be a collection of collections"); + } + if (theInnerCollectionType != null) { + myInnerCollectionType = CollectionBinder.getInstantiableCollectionType(theInnerCollectionType, SummaryEnum.class.getSimpleName()); + } + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index 1a28ffc0e9c..f678f92309d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -51,6 +51,7 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Count; +import ca.uhn.fhir.rest.annotation.Elements; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -444,6 +445,8 @@ public class MethodUtil { param = new NullParameter(); } else if (nextAnnotation instanceof ServerBase) { param = new ServerBaseParamBinder(); + } else if (nextAnnotation instanceof Elements) { + param = new ElementsParameter(); } else if (nextAnnotation instanceof Since) { param = new SinceParameter(); } else if (nextAnnotation instanceof Count) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java index 8be6efc006f..4805c990855 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java @@ -41,6 +41,7 @@ import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; +import ca.uhn.fhir.rest.annotation.Elements; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.RequestTypeEnum; @@ -105,6 +106,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem public List> getAllowableParamAnnotations() { ArrayList> retVal = new ArrayList>(); retVal.add(IdParam.class); + retVal.add(Elements.class); return retVal; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 07efd6cda5c..a400bc9a851 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -62,6 +62,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.ConformanceMethodBinding; +import ca.uhn.fhir.rest.method.ElementsParameter; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; @@ -451,7 +452,9 @@ public class RestfulServer extends HttpServlet { boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest()); Set summaryMode = RestfulServerUtils.determineSummaryMode(theRequest); boolean respondGzip = theRequest.isRespondGzip(); - + Set elements = ElementsParameter.getElementsValueOrNull(theRequest); + Set elementsAppliesTo = null; // TODO: persist this across pages + IVersionSpecificBundleFactory bundleFactory = getFhirContext().newBundleFactory(); Set includes = new HashSet(); @@ -489,7 +492,7 @@ public class RestfulServer extends HttpServlet { } } RestfulServerUtils.streamResponseAsResource(this, theResponse, resBundle, responseEncoding, prettyPrint, requestIsBrowser, summaryMode, Constants.STATUS_HTTP_200_OK, - theRequest.isRespondGzip(), theRequest.getFhirServerBase(), false); + theRequest.isRespondGzip(), theRequest.getFhirServerBase(), false, elements, elementsAppliesTo); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index 2d790708f6e..bd04620e329 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -410,7 +410,7 @@ public class RestfulServerUtils { } public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IBaseResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, Set theNarrativeMode, int stausCode, boolean theRespondGzip, - String theServerBase, boolean theAddContentLocationHeader) throws IOException { + String theServerBase, boolean theAddContentLocationHeader, Set theElements, Set theElementsAppliesTo) throws IOException { theHttpResponse.setStatus(stausCode); if (theAddContentLocationHeader && theResource.getIdElement() != null && theResource.getIdElement().hasIdPart() && isNotBlank(theServerBase)) { @@ -506,6 +506,14 @@ public class RestfulServerUtils { } else { IParser parser = getNewParser(theServer.getFhirContext(), responseEncoding, thePrettyPrint, theNarrativeMode); parser.setServerBaseUrl(theServerBase); + if (theElements != null && theElements.size() > 0) { + Set elements = new HashSet(); + for (String next : theElements) { + elements.add("*." + next); + } + parser.setEncodeElements(elements); + parser.setEncodeElementsAppliesToResourceTypes(theElementsAppliesTo); + } parser.encodeResourceToWriter(theResource, writer); } } finally { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java index 0b0fe045ce3..0c7b83ded32 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java @@ -75,7 +75,7 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter { boolean requestIsBrowser = RestfulServer.requestIsBrowser(theRequest); String fhirServerBase = theRequestDetails.getFhirServerBase(); - RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser, Collections.singleton(SummaryEnum.FALSE), statusCode, false, fhirServerBase, false); + RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser, Collections.singleton(SummaryEnum.FALSE), statusCode, false, fhirServerBase, false, null, null); // theResponse.setStatus(statusCode); // theRequestDetails.getServer().addHeadersToResponse(theResponse); @@ -95,7 +95,7 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter { } else { retVal = (BaseServerResponseException) theException; } - + if (retVal.getOperationOutcome() == null) { retVal.setOperationOutcome(createOperationOutcome(theException, theRequestDetails.getServer().getFhirContext())); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseMetaType.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseMetaType.java index 66e28e58ba5..9b9a890fa25 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseMetaType.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseMetaType.java @@ -32,6 +32,4 @@ public interface IBaseMetaType extends ICompositeType { String getVersionId(); - IBaseMetaType copy(); - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 0f31e6412af..06f75192cbf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -1220,14 +1220,22 @@ public abstract class BaseHapiFhirDao implements IDao { /** * This method is invoked immediately before storing a new resource, or an * update to an existing resource to allow the DAO to ensure that it is valid - * for persistence. By default, no validation is performed, but subclasses - * may override to provide specific behaviour. + * for persistence. By default, checks for the "subsetted" tag and rejects + * resources which have it. Subclasses should call the superclass implementation to + * preserve this check. * * @param theResource * The resource that is about to be persisted */ protected void validateResourceForStorage(T theResource) { - // nothing + IResource res = (IResource)theResource; + TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res); + if (tagList != null) { + Tag tag = tagList.getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE); + if (tag != null) { + throw new UnprocessableEntityException("Resource contains the 'subsetted' tag, and must not be stored as it may contain a subset of available data"); + } + } } protected static String normalizeString(String theString) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java index 8e21c863147..90b3db79eca 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java @@ -83,6 +83,7 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; @@ -309,6 +310,25 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest { } } + + @Test + public void testCreateSummaryFails() { + Patient p = new Patient(); + p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails"); + p.addName().addFamily("Hello"); + + TagList tl = new TagList(); + tl.addTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE); + ResourceMetadataKeyEnum.TAG_LIST.put(p, tl); + + try { + ourPatientDao.create(p); + fail(); + } catch (UnprocessableEntityException e) { + assertThat(e.getMessage(), containsString("subsetted")); + } + } + @Test public void testCreateWithIfNoneExistBasic() { String methodName = "testCreateWithIfNoneExistBasic"; diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientDstu1Test.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientDstu1Test.java index 3c98b80d477..fa32eeda1e7 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientDstu1Test.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientDstu1Test.java @@ -11,7 +11,9 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; @@ -51,6 +53,7 @@ import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.IntegerDt; +import ca.uhn.fhir.rest.annotation.Elements; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.RequiredParam; @@ -827,6 +830,51 @@ public class ClientDstu1Test { idx++; } + @Test + public void testSearchWithElements() throws Exception { + + final String msg = getPatientFeedWithOneResult(); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + + when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(httpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + } + }); + + // httpResponse = new BasicHttpResponse(statusline, catalog, locale) + when(httpClient.execute(capt.capture())).thenReturn(httpResponse); + + ITestClientWithElements client = ourCtx.newRestfulClient(ITestClientWithElements.class, "http://foo"); + + int idx = 0; + + client.getPatientWithIncludes((String) null); + assertEquals("http://foo/Patient", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + client.getPatientWithIncludes((Set) null); + assertEquals("http://foo/Patient", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + client.getPatientWithIncludes("test"); + assertEquals("http://foo/Patient?_elements=test", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + client.getPatientWithIncludes("test,foo"); + assertEquals("http://foo/Patient?_elements=test%2Cfoo", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + client.getPatientWithIncludes(new HashSet(Arrays.asList("test","foo", ""))); + assertEquals("http://foo/Patient?_elements=test%2Cfoo", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + } + @Test public void testSearchByCompartment() throws Exception { @@ -1316,4 +1364,13 @@ public class ClientDstu1Test { } + public interface ITestClientWithElements extends IBasicClient { + @Search() + public List getPatientWithIncludes(@Elements String theElements); + + @Search() + public List getPatientWithIncludes(@Elements Set theElements); + + } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ElementsParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ElementsParamTest.java new file mode 100644 index 00000000000..c7ed71d4443 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ElementsParamTest.java @@ -0,0 +1,179 @@ +package ca.uhn.fhir.rest.server; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.util.PortUtil; + +public class ElementsParamTest { + + private static CloseableHttpClient ourClient; + private static FhirContext ourCtx = FhirContext.forDstu2(); + private static Set ourLastElements; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElementsParamTest.class); + private static int ourPort; + private static Server ourServer; + + @Before + public void before() { + ourLastElements = null; + } + + @Test + public void testReadSummaryData() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_elements=name,maritalStatus"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + + assertEquals(200, status.getStatusLine().getStatusCode()); + assertEquals(Constants.CT_FHIR_XML + Constants.CHARSET_UTF8_CTSUFFIX, status.getEntity().getContentType().getValue()); + assertThat(responseContent, not(containsString("THE DIV"))); + assertThat(responseContent, (containsString("family"))); + assertThat(responseContent, (containsString("maritalStatus"))); + assertThat(ourLastElements, containsInAnyOrder("name", "maritalStatus")); + } + + @Test + public void testReadSummaryTrue() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_elements=name"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + + assertEquals(200, status.getStatusLine().getStatusCode()); + assertEquals(Constants.CT_FHIR_XML + Constants.CHARSET_UTF8_CTSUFFIX, status.getEntity().getContentType().getValue()); + assertThat(responseContent, not(containsString("THE DIV"))); + assertThat(responseContent, (containsString("family"))); + assertThat(responseContent, not(containsString("maritalStatus"))); + assertThat(ourLastElements, containsInAnyOrder("name")); + } + + @Test + public void testSearchSummaryData() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_elements=name,maritalStatus"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString(""))); + assertThat(responseContent, (containsString("entry"))); + assertThat(responseContent, (containsString("THE DIV"))); + assertThat(responseContent, not(containsString("family"))); + assertThat(responseContent, not(containsString("maritalStatus"))); + assertThat(ourLastElements, containsInAnyOrder("text")); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ourPort = PortUtil.findFreePort(); + ourServer = new Server(ourPort); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + RestfulServer servlet = new RestfulServer(ourCtx); + + servlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + ourServer.start(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Read + public Patient read(@IdParam IdDt theId, @Elements Set theElements) { + ourLastElements = theElements; + Patient patient = new Patient(); + patient.setId("Patient/1/_history/1"); + patient.getText().setDiv("
THE DIV
"); + patient.addName().addFamily("FAMILY"); + patient.setMaritalStatus(MaritalStatusCodesEnum.D); + return patient; + } + + @Search() + public Patient search(@Elements Set theElements) { + ourLastElements = theElements; + Patient patient = new Patient(); + patient.setId("Patient/1/_history/1"); + patient.getText().setDiv("
THE DIV
"); + patient.addName().addFamily("FAMILY"); + patient.setMaritalStatus(MaritalStatusCodesEnum.D); + return patient; + } + + } + +} diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/QuestionnaireAnswers.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/QuestionnaireAnswers.java deleted file mode 100644 index 47d7fe96295..00000000000 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/QuestionnaireAnswers.java +++ /dev/null @@ -1,1572 +0,0 @@ -package org.hl7.fhir.instance.model; - -/* - 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: - - * Redistributions of source code must retain the above copyright notice, this - 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. - * 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. - - 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. - -*/ - -// Generated on Thu, Jul 23, 2015 16:50-0400 for FHIR v0.5.0 - -import java.util.*; - -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.instance.model.annotations.ResourceDef; -import org.hl7.fhir.instance.model.annotations.SearchParamDefinition; -import org.hl7.fhir.instance.model.annotations.Child; -import org.hl7.fhir.instance.model.annotations.Description; -import org.hl7.fhir.instance.model.annotations.Block; -import org.hl7.fhir.instance.model.api.*; -/** - * A structured set of questions and their answers. The questions are ordered and grouped into coherent subsets, corresponding to the structure of the grouping of the underlying questions. - */ -@ResourceDef(name="QuestionnaireAnswers", profile="http://hl7.org/fhir/Profile/QuestionnaireAnswers") -public class QuestionnaireAnswers extends DomainResource { - - public enum QuestionnaireAnswersStatus { - /** - * This QuestionnaireAnswers has been partially filled out with answers, but changes or additions are still expected to be made to it. - */ - INPROGRESS, - /** - * This QuestionnaireAnswers has been filled out with answers, and the current content is regarded as definitive. - */ - COMPLETED, - /** - * This QuestionnaireAnswers has been filled out with answers, then marked as complete, yet changes or additions have been made to it afterwards. - */ - AMENDED, - /** - * added to help the parsers - */ - NULL; - public static QuestionnaireAnswersStatus fromCode(String codeString) throws Exception { - if (codeString == null || "".equals(codeString)) - return null; - if ("in-progress".equals(codeString)) - return INPROGRESS; - if ("completed".equals(codeString)) - return COMPLETED; - if ("amended".equals(codeString)) - return AMENDED; - throw new Exception("Unknown QuestionnaireAnswersStatus code '"+codeString+"'"); - } - public String toCode() { - switch (this) { - case INPROGRESS: return "in-progress"; - case COMPLETED: return "completed"; - case AMENDED: return "amended"; - default: return "?"; - } - } - public String getSystem() { - switch (this) { - case INPROGRESS: return "http://hl7.org/fhir/questionnaire-answers-status"; - case COMPLETED: return "http://hl7.org/fhir/questionnaire-answers-status"; - case AMENDED: return "http://hl7.org/fhir/questionnaire-answers-status"; - default: return "?"; - } - } - public String getDefinition() { - switch (this) { - case INPROGRESS: return "This QuestionnaireAnswers has been partially filled out with answers, but changes or additions are still expected to be made to it."; - case COMPLETED: return "This QuestionnaireAnswers has been filled out with answers, and the current content is regarded as definitive."; - case AMENDED: return "This QuestionnaireAnswers has been filled out with answers, then marked as complete, yet changes or additions have been made to it afterwards."; - default: return "?"; - } - } - public String getDisplay() { - switch (this) { - case INPROGRESS: return "In Progress"; - case COMPLETED: return "Completed"; - case AMENDED: return "Amended"; - default: return "?"; - } - } - } - - public static class QuestionnaireAnswersStatusEnumFactory implements EnumFactory { - public QuestionnaireAnswersStatus fromCode(String codeString) throws IllegalArgumentException { - if (codeString == null || "".equals(codeString)) - if (codeString == null || "".equals(codeString)) - return null; - if ("in-progress".equals(codeString)) - return QuestionnaireAnswersStatus.INPROGRESS; - if ("completed".equals(codeString)) - return QuestionnaireAnswersStatus.COMPLETED; - if ("amended".equals(codeString)) - return QuestionnaireAnswersStatus.AMENDED; - throw new IllegalArgumentException("Unknown QuestionnaireAnswersStatus code '"+codeString+"'"); - } - public String toCode(QuestionnaireAnswersStatus code) { - if (code == QuestionnaireAnswersStatus.INPROGRESS) - return "in-progress"; - if (code == QuestionnaireAnswersStatus.COMPLETED) - return "completed"; - if (code == QuestionnaireAnswersStatus.AMENDED) - return "amended"; - return "?"; - } - } - - @Block() - public static class GroupComponent extends BackboneElement implements IBaseBackboneElement { - /** - * Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource. - */ - @Child(name = "linkId", type = {StringType.class}, order=1, min=0, max=1) - @Description(shortDefinition="Corresponding group within Questionnaire", formalDefinition="Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource." ) - protected StringType linkId; - - /** - * Text that is displayed above the contents of the group. - */ - @Child(name = "title", type = {StringType.class}, order=2, min=0, max=1) - @Description(shortDefinition="Name for this group", formalDefinition="Text that is displayed above the contents of the group." ) - protected StringType title; - - /** - * Additional text for the group, used for display purposes. - */ - @Child(name = "text", type = {StringType.class}, order=3, min=0, max=1) - @Description(shortDefinition="Additional text for the group", formalDefinition="Additional text for the group, used for display purposes." ) - protected StringType text; - - /** - * More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers. - */ - @Child(name = "subject", type = {}, order=4, min=0, max=1) - @Description(shortDefinition="The subject this group's answers are about", formalDefinition="More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers." ) - protected Reference subject; - - /** - * The actual object that is the target of the reference (More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers.) - */ - protected Resource subjectTarget; - - /** - * A sub-group within a group. The ordering of groups within this group is relevant. - */ - @Child(name = "group", type = {GroupComponent.class}, order=5, min=0, max=Child.MAX_UNLIMITED) - @Description(shortDefinition="Nested questionnaire answers group", formalDefinition="A sub-group within a group. The ordering of groups within this group is relevant." ) - protected List group; - - /** - * Set of questions within this group. The order of questions within the group is relevant. - */ - @Child(name = "question", type = {}, order=6, min=0, max=Child.MAX_UNLIMITED) - @Description(shortDefinition="Questions in this group", formalDefinition="Set of questions within this group. The order of questions within the group is relevant." ) - protected List question; - - private static final long serialVersionUID = -1045990435L; - - /* - * Constructor - */ - public GroupComponent() { - super(); - } - - /** - * @return {@link #linkId} (Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource.). This is the underlying object with id, value and extensions. The accessor "getLinkId" gives direct access to the value - */ - public StringType getLinkIdElement() { - if (this.linkId == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create GroupComponent.linkId"); - else if (Configuration.doAutoCreate()) - this.linkId = new StringType(); // bb - return this.linkId; - } - - public boolean hasLinkIdElement() { - return this.linkId != null && !this.linkId.isEmpty(); - } - - public boolean hasLinkId() { - return this.linkId != null && !this.linkId.isEmpty(); - } - - /** - * @param value {@link #linkId} (Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource.). This is the underlying object with id, value and extensions. The accessor "getLinkId" gives direct access to the value - */ - public GroupComponent setLinkIdElement(StringType value) { - this.linkId = value; - return this; - } - - /** - * @return Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource. - */ - public String getLinkId() { - return this.linkId == null ? null : this.linkId.getValue(); - } - - /** - * @param value Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource. - */ - public GroupComponent setLinkId(String value) { - if (Utilities.noString(value)) - this.linkId = null; - else { - if (this.linkId == null) - this.linkId = new StringType(); - this.linkId.setValue(value); - } - return this; - } - - /** - * @return {@link #title} (Text that is displayed above the contents of the group.). This is the underlying object with id, value and extensions. The accessor "getTitle" gives direct access to the value - */ - public StringType getTitleElement() { - if (this.title == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create GroupComponent.title"); - else if (Configuration.doAutoCreate()) - this.title = new StringType(); // bb - return this.title; - } - - public boolean hasTitleElement() { - return this.title != null && !this.title.isEmpty(); - } - - public boolean hasTitle() { - return this.title != null && !this.title.isEmpty(); - } - - /** - * @param value {@link #title} (Text that is displayed above the contents of the group.). This is the underlying object with id, value and extensions. The accessor "getTitle" gives direct access to the value - */ - public GroupComponent setTitleElement(StringType value) { - this.title = value; - return this; - } - - /** - * @return Text that is displayed above the contents of the group. - */ - public String getTitle() { - return this.title == null ? null : this.title.getValue(); - } - - /** - * @param value Text that is displayed above the contents of the group. - */ - public GroupComponent setTitle(String value) { - if (Utilities.noString(value)) - this.title = null; - else { - if (this.title == null) - this.title = new StringType(); - this.title.setValue(value); - } - return this; - } - - /** - * @return {@link #text} (Additional text for the group, used for display purposes.). This is the underlying object with id, value and extensions. The accessor "getText" gives direct access to the value - */ - public StringType getTextElement() { - if (this.text == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create GroupComponent.text"); - else if (Configuration.doAutoCreate()) - this.text = new StringType(); // bb - return this.text; - } - - public boolean hasTextElement() { - return this.text != null && !this.text.isEmpty(); - } - - public boolean hasText() { - return this.text != null && !this.text.isEmpty(); - } - - /** - * @param value {@link #text} (Additional text for the group, used for display purposes.). This is the underlying object with id, value and extensions. The accessor "getText" gives direct access to the value - */ - public GroupComponent setTextElement(StringType value) { - this.text = value; - return this; - } - - /** - * @return Additional text for the group, used for display purposes. - */ - public String getText() { - return this.text == null ? null : this.text.getValue(); - } - - /** - * @param value Additional text for the group, used for display purposes. - */ - public GroupComponent setText(String value) { - if (Utilities.noString(value)) - this.text = null; - else { - if (this.text == null) - this.text = new StringType(); - this.text.setValue(value); - } - return this; - } - - /** - * @return {@link #subject} (More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers.) - */ - public Reference getSubject() { - if (this.subject == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create GroupComponent.subject"); - else if (Configuration.doAutoCreate()) - this.subject = new Reference(); // cc - return this.subject; - } - - public boolean hasSubject() { - return this.subject != null && !this.subject.isEmpty(); - } - - /** - * @param value {@link #subject} (More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers.) - */ - public GroupComponent setSubject(Reference value) { - this.subject = value; - return this; - } - - /** - * @return {@link #subject} The actual object that is the target of the reference. The reference library doesn't populate this, but you can use it to hold the resource if you resolve it. (More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers.) - */ - public Resource getSubjectTarget() { - return this.subjectTarget; - } - - /** - * @param value {@link #subject} The actual object that is the target of the reference. The reference library doesn't use these, but you can use it to hold the resource if you resolve it. (More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers.) - */ - public GroupComponent setSubjectTarget(Resource value) { - this.subjectTarget = value; - return this; - } - - /** - * @return {@link #group} (A sub-group within a group. The ordering of groups within this group is relevant.) - */ - public List getGroup() { - if (this.group == null) - this.group = new ArrayList(); - return this.group; - } - - public boolean hasGroup() { - if (this.group == null) - return false; - for (GroupComponent item : this.group) - if (!item.isEmpty()) - return true; - return false; - } - - /** - * @return {@link #group} (A sub-group within a group. The ordering of groups within this group is relevant.) - */ - // syntactic sugar - public GroupComponent addGroup() { //3 - GroupComponent t = new GroupComponent(); - if (this.group == null) - this.group = new ArrayList(); - this.group.add(t); - return t; - } - - // syntactic sugar - public GroupComponent addGroup(GroupComponent t) { //3 - if (t == null) - return this; - if (this.group == null) - this.group = new ArrayList(); - this.group.add(t); - return this; - } - - /** - * @return {@link #question} (Set of questions within this group. The order of questions within the group is relevant.) - */ - public List getQuestion() { - if (this.question == null) - this.question = new ArrayList(); - return this.question; - } - - public boolean hasQuestion() { - if (this.question == null) - return false; - for (QuestionComponent item : this.question) - if (!item.isEmpty()) - return true; - return false; - } - - /** - * @return {@link #question} (Set of questions within this group. The order of questions within the group is relevant.) - */ - // syntactic sugar - public QuestionComponent addQuestion() { //3 - QuestionComponent t = new QuestionComponent(); - if (this.question == null) - this.question = new ArrayList(); - this.question.add(t); - return t; - } - - // syntactic sugar - public GroupComponent addQuestion(QuestionComponent t) { //3 - if (t == null) - return this; - if (this.question == null) - this.question = new ArrayList(); - this.question.add(t); - return this; - } - - protected void listChildren(List childrenList) { - super.listChildren(childrenList); - childrenList.add(new Property("linkId", "string", "Identifies the group from the Questionnaire that corresponds to this group in the QuestionnaireAnswers resource.", 0, java.lang.Integer.MAX_VALUE, linkId)); - childrenList.add(new Property("title", "string", "Text that is displayed above the contents of the group.", 0, java.lang.Integer.MAX_VALUE, title)); - childrenList.add(new Property("text", "string", "Additional text for the group, used for display purposes.", 0, java.lang.Integer.MAX_VALUE, text)); - childrenList.add(new Property("subject", "Reference(Any)", "More specific subject this section's answers are about, details the subject given in QuestionnaireAnswers.", 0, java.lang.Integer.MAX_VALUE, subject)); - childrenList.add(new Property("group", "@QuestionnaireAnswers.group", "A sub-group within a group. The ordering of groups within this group is relevant.", 0, java.lang.Integer.MAX_VALUE, group)); - childrenList.add(new Property("question", "", "Set of questions within this group. The order of questions within the group is relevant.", 0, java.lang.Integer.MAX_VALUE, question)); - } - - public GroupComponent copy() { - GroupComponent dst = new GroupComponent(); - copyValues(dst); - dst.linkId = linkId == null ? null : linkId.copy(); - dst.title = title == null ? null : title.copy(); - dst.text = text == null ? null : text.copy(); - dst.subject = subject == null ? null : subject.copy(); - if (group != null) { - dst.group = new ArrayList(); - for (GroupComponent i : group) - dst.group.add(i.copy()); - }; - if (question != null) { - dst.question = new ArrayList(); - for (QuestionComponent i : question) - dst.question.add(i.copy()); - }; - return dst; - } - - @Override - public boolean equalsDeep(Base other) { - if (!super.equalsDeep(other)) - return false; - if (!(other instanceof GroupComponent)) - return false; - GroupComponent o = (GroupComponent) other; - return compareDeep(linkId, o.linkId, true) && compareDeep(title, o.title, true) && compareDeep(text, o.text, true) - && compareDeep(subject, o.subject, true) && compareDeep(group, o.group, true) && compareDeep(question, o.question, true) - ; - } - - @Override - public boolean equalsShallow(Base other) { - if (!super.equalsShallow(other)) - return false; - if (!(other instanceof GroupComponent)) - return false; - GroupComponent o = (GroupComponent) other; - return compareValues(linkId, o.linkId, true) && compareValues(title, o.title, true) && compareValues(text, o.text, true) - ; - } - - public boolean isEmpty() { - return super.isEmpty() && (linkId == null || linkId.isEmpty()) && (title == null || title.isEmpty()) - && (text == null || text.isEmpty()) && (subject == null || subject.isEmpty()) && (group == null || group.isEmpty()) - && (question == null || question.isEmpty()); - } - - } - - @Block() - public static class QuestionComponent extends BackboneElement implements IBaseBackboneElement { - /** - * Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource. - */ - @Child(name = "linkId", type = {StringType.class}, order=1, min=0, max=1) - @Description(shortDefinition="Corresponding question within Questionnaire", formalDefinition="Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource." ) - protected StringType linkId; - - /** - * The actual question as shown to the user to prompt them for an answer. - */ - @Child(name = "text", type = {StringType.class}, order=2, min=0, max=1) - @Description(shortDefinition="Text of the question as it is shown to the user", formalDefinition="The actual question as shown to the user to prompt them for an answer." ) - protected StringType text; - - /** - * The respondent's answer(s) to the question. - */ - @Child(name = "answer", type = {}, order=3, min=0, max=Child.MAX_UNLIMITED) - @Description(shortDefinition="The response(s) to the question", formalDefinition="The respondent's answer(s) to the question." ) - protected List answer; - - /** - * Nested group, containing nested question for this question. The order of groups within the question is relevant. - */ - @Child(name = "group", type = {GroupComponent.class}, order=4, min=0, max=Child.MAX_UNLIMITED) - @Description(shortDefinition="Nested questionnaire group", formalDefinition="Nested group, containing nested question for this question. The order of groups within the question is relevant." ) - protected List group; - - private static final long serialVersionUID = -564009278L; - - /* - * Constructor - */ - public QuestionComponent() { - super(); - } - - /** - * @return {@link #linkId} (Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource.). This is the underlying object with id, value and extensions. The accessor "getLinkId" gives direct access to the value - */ - public StringType getLinkIdElement() { - if (this.linkId == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionComponent.linkId"); - else if (Configuration.doAutoCreate()) - this.linkId = new StringType(); // bb - return this.linkId; - } - - public boolean hasLinkIdElement() { - return this.linkId != null && !this.linkId.isEmpty(); - } - - public boolean hasLinkId() { - return this.linkId != null && !this.linkId.isEmpty(); - } - - /** - * @param value {@link #linkId} (Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource.). This is the underlying object with id, value and extensions. The accessor "getLinkId" gives direct access to the value - */ - public QuestionComponent setLinkIdElement(StringType value) { - this.linkId = value; - return this; - } - - /** - * @return Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource. - */ - public String getLinkId() { - return this.linkId == null ? null : this.linkId.getValue(); - } - - /** - * @param value Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource. - */ - public QuestionComponent setLinkId(String value) { - if (Utilities.noString(value)) - this.linkId = null; - else { - if (this.linkId == null) - this.linkId = new StringType(); - this.linkId.setValue(value); - } - return this; - } - - /** - * @return {@link #text} (The actual question as shown to the user to prompt them for an answer.). This is the underlying object with id, value and extensions. The accessor "getText" gives direct access to the value - */ - public StringType getTextElement() { - if (this.text == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionComponent.text"); - else if (Configuration.doAutoCreate()) - this.text = new StringType(); // bb - return this.text; - } - - public boolean hasTextElement() { - return this.text != null && !this.text.isEmpty(); - } - - public boolean hasText() { - return this.text != null && !this.text.isEmpty(); - } - - /** - * @param value {@link #text} (The actual question as shown to the user to prompt them for an answer.). This is the underlying object with id, value and extensions. The accessor "getText" gives direct access to the value - */ - public QuestionComponent setTextElement(StringType value) { - this.text = value; - return this; - } - - /** - * @return The actual question as shown to the user to prompt them for an answer. - */ - public String getText() { - return this.text == null ? null : this.text.getValue(); - } - - /** - * @param value The actual question as shown to the user to prompt them for an answer. - */ - public QuestionComponent setText(String value) { - if (Utilities.noString(value)) - this.text = null; - else { - if (this.text == null) - this.text = new StringType(); - this.text.setValue(value); - } - return this; - } - - /** - * @return {@link #answer} (The respondent's answer(s) to the question.) - */ - public List getAnswer() { - if (this.answer == null) - this.answer = new ArrayList(); - return this.answer; - } - - public boolean hasAnswer() { - if (this.answer == null) - return false; - for (QuestionAnswerComponent item : this.answer) - if (!item.isEmpty()) - return true; - return false; - } - - /** - * @return {@link #answer} (The respondent's answer(s) to the question.) - */ - // syntactic sugar - public QuestionAnswerComponent addAnswer() { //3 - QuestionAnswerComponent t = new QuestionAnswerComponent(); - if (this.answer == null) - this.answer = new ArrayList(); - this.answer.add(t); - return t; - } - - // syntactic sugar - public QuestionComponent addAnswer(QuestionAnswerComponent t) { //3 - if (t == null) - return this; - if (this.answer == null) - this.answer = new ArrayList(); - this.answer.add(t); - return this; - } - - /** - * @return {@link #group} (Nested group, containing nested question for this question. The order of groups within the question is relevant.) - */ - public List getGroup() { - if (this.group == null) - this.group = new ArrayList(); - return this.group; - } - - public boolean hasGroup() { - if (this.group == null) - return false; - for (GroupComponent item : this.group) - if (!item.isEmpty()) - return true; - return false; - } - - /** - * @return {@link #group} (Nested group, containing nested question for this question. The order of groups within the question is relevant.) - */ - // syntactic sugar - public GroupComponent addGroup() { //3 - GroupComponent t = new GroupComponent(); - if (this.group == null) - this.group = new ArrayList(); - this.group.add(t); - return t; - } - - // syntactic sugar - public QuestionComponent addGroup(GroupComponent t) { //3 - if (t == null) - return this; - if (this.group == null) - this.group = new ArrayList(); - this.group.add(t); - return this; - } - - protected void listChildren(List childrenList) { - super.listChildren(childrenList); - childrenList.add(new Property("linkId", "string", "Identifies the question from the Questionnaire that corresponds to this question in the QuestionnaireAnswers resource.", 0, java.lang.Integer.MAX_VALUE, linkId)); - childrenList.add(new Property("text", "string", "The actual question as shown to the user to prompt them for an answer.", 0, java.lang.Integer.MAX_VALUE, text)); - childrenList.add(new Property("answer", "", "The respondent's answer(s) to the question.", 0, java.lang.Integer.MAX_VALUE, answer)); - childrenList.add(new Property("group", "@QuestionnaireAnswers.group", "Nested group, containing nested question for this question. The order of groups within the question is relevant.", 0, java.lang.Integer.MAX_VALUE, group)); - } - - public QuestionComponent copy() { - QuestionComponent dst = new QuestionComponent(); - copyValues(dst); - dst.linkId = linkId == null ? null : linkId.copy(); - dst.text = text == null ? null : text.copy(); - if (answer != null) { - dst.answer = new ArrayList(); - for (QuestionAnswerComponent i : answer) - dst.answer.add(i.copy()); - }; - if (group != null) { - dst.group = new ArrayList(); - for (GroupComponent i : group) - dst.group.add(i.copy()); - }; - return dst; - } - - @Override - public boolean equalsDeep(Base other) { - if (!super.equalsDeep(other)) - return false; - if (!(other instanceof QuestionComponent)) - return false; - QuestionComponent o = (QuestionComponent) other; - return compareDeep(linkId, o.linkId, true) && compareDeep(text, o.text, true) && compareDeep(answer, o.answer, true) - && compareDeep(group, o.group, true); - } - - @Override - public boolean equalsShallow(Base other) { - if (!super.equalsShallow(other)) - return false; - if (!(other instanceof QuestionComponent)) - return false; - QuestionComponent o = (QuestionComponent) other; - return compareValues(linkId, o.linkId, true) && compareValues(text, o.text, true); - } - - public boolean isEmpty() { - return super.isEmpty() && (linkId == null || linkId.isEmpty()) && (text == null || text.isEmpty()) - && (answer == null || answer.isEmpty()) && (group == null || group.isEmpty()); - } - - } - - @Block() - public static class QuestionAnswerComponent extends BackboneElement implements IBaseBackboneElement { - /** - * The answer (or one of the answers) provided by the respondant to the question. - */ - @Child(name = "value", type = {BooleanType.class, DecimalType.class, IntegerType.class, DateType.class, DateTimeType.class, InstantType.class, TimeType.class, StringType.class, UriType.class, Attachment.class, Coding.class, Quantity.class}, order=1, min=0, max=1) - @Description(shortDefinition="Single-valued answer to the question", formalDefinition="The answer (or one of the answers) provided by the respondant to the question." ) - protected Type value; - - private static final long serialVersionUID = -732981989L; - - /* - * Constructor - */ - public QuestionAnswerComponent() { - super(); - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public Type getValue() { - return this.value; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public BooleanType getValueBooleanType() throws Exception { - if (!(this.value instanceof BooleanType)) - throw new Exception("Type mismatch: the type BooleanType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (BooleanType) this.value; - } - - public boolean hasValueBooleanType() throws Exception { - return this.value instanceof BooleanType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public DecimalType getValueDecimalType() throws Exception { - if (!(this.value instanceof DecimalType)) - throw new Exception("Type mismatch: the type DecimalType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (DecimalType) this.value; - } - - public boolean hasValueDecimalType() throws Exception { - return this.value instanceof DecimalType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public IntegerType getValueIntegerType() throws Exception { - if (!(this.value instanceof IntegerType)) - throw new Exception("Type mismatch: the type IntegerType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (IntegerType) this.value; - } - - public boolean hasValueIntegerType() throws Exception { - return this.value instanceof IntegerType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public DateType getValueDateType() throws Exception { - if (!(this.value instanceof DateType)) - throw new Exception("Type mismatch: the type DateType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (DateType) this.value; - } - - public boolean hasValueDateType() throws Exception { - return this.value instanceof DateType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public DateTimeType getValueDateTimeType() throws Exception { - if (!(this.value instanceof DateTimeType)) - throw new Exception("Type mismatch: the type DateTimeType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (DateTimeType) this.value; - } - - public boolean hasValueDateTimeType() throws Exception { - return this.value instanceof DateTimeType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public InstantType getValueInstantType() throws Exception { - if (!(this.value instanceof InstantType)) - throw new Exception("Type mismatch: the type InstantType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (InstantType) this.value; - } - - public boolean hasValueInstantType() throws Exception { - return this.value instanceof InstantType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public TimeType getValueTimeType() throws Exception { - if (!(this.value instanceof TimeType)) - throw new Exception("Type mismatch: the type TimeType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (TimeType) this.value; - } - - public boolean hasValueTimeType() throws Exception { - return this.value instanceof TimeType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public StringType getValueStringType() throws Exception { - if (!(this.value instanceof StringType)) - throw new Exception("Type mismatch: the type StringType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (StringType) this.value; - } - - public boolean hasValueStringType() throws Exception { - return this.value instanceof StringType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public UriType getValueUriType() throws Exception { - if (!(this.value instanceof UriType)) - throw new Exception("Type mismatch: the type UriType was expected, but "+this.value.getClass().getName()+" was encountered"); - return (UriType) this.value; - } - - public boolean hasValueUriType() throws Exception { - return this.value instanceof UriType; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public Attachment getValueAttachment() throws Exception { - if (!(this.value instanceof Attachment)) - throw new Exception("Type mismatch: the type Attachment was expected, but "+this.value.getClass().getName()+" was encountered"); - return (Attachment) this.value; - } - - public boolean hasValueAttachment() throws Exception { - return this.value instanceof Attachment; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public Coding getValueCoding() throws Exception { - if (!(this.value instanceof Coding)) - throw new Exception("Type mismatch: the type Coding was expected, but "+this.value.getClass().getName()+" was encountered"); - return (Coding) this.value; - } - - public boolean hasValueCoding() throws Exception { - return this.value instanceof Coding; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public Quantity getValueQuantity() throws Exception { - if (!(this.value instanceof Quantity)) - throw new Exception("Type mismatch: the type Quantity was expected, but "+this.value.getClass().getName()+" was encountered"); - return (Quantity) this.value; - } - - public boolean hasValueQuantity() throws Exception { - return this.value instanceof Quantity; - } - - /** - * @return {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public Reference getValueReference() throws Exception { - if (!(this.value instanceof Reference)) - throw new Exception("Type mismatch: the type Reference was expected, but "+this.value.getClass().getName()+" was encountered"); - return (Reference) this.value; - } - - public boolean hasValueReference() throws Exception { - return this.value instanceof Reference; - } - - public boolean hasValue() { - return this.value != null && !this.value.isEmpty(); - } - - /** - * @param value {@link #value} (The answer (or one of the answers) provided by the respondant to the question.) - */ - public QuestionAnswerComponent setValue(Type value) { - this.value = value; - return this; - } - - protected void listChildren(List childrenList) { - super.listChildren(childrenList); - childrenList.add(new Property("value[x]", "boolean|decimal|integer|date|dateTime|instant|time|string|uri|Attachment|Coding|Quantity|Reference(Any)", "The answer (or one of the answers) provided by the respondant to the question.", 0, java.lang.Integer.MAX_VALUE, value)); - } - - public QuestionAnswerComponent copy() { - QuestionAnswerComponent dst = new QuestionAnswerComponent(); - copyValues(dst); - dst.value = value == null ? null : value.copy(); - return dst; - } - - @Override - public boolean equalsDeep(Base other) { - if (!super.equalsDeep(other)) - return false; - if (!(other instanceof QuestionAnswerComponent)) - return false; - QuestionAnswerComponent o = (QuestionAnswerComponent) other; - return compareDeep(value, o.value, true); - } - - @Override - public boolean equalsShallow(Base other) { - if (!super.equalsShallow(other)) - return false; - if (!(other instanceof QuestionAnswerComponent)) - return false; - QuestionAnswerComponent o = (QuestionAnswerComponent) other; - return true; - } - - public boolean isEmpty() { - return super.isEmpty() && (value == null || value.isEmpty()); - } - - } - - /** - * A business identifier assigned to a particular completed (or partially completed) questionnaire. - */ - @Child(name = "identifier", type = {Identifier.class}, order=0, min=0, max=1) - @Description(shortDefinition="Unique id for this set of answers", formalDefinition="A business identifier assigned to a particular completed (or partially completed) questionnaire." ) - protected Identifier identifier; - - /** - * Indicates the Questionnaire resource that defines the form for which answers are being provided. - */ - @Child(name = "questionnaire", type = {Questionnaire.class}, order=1, min=0, max=1) - @Description(shortDefinition="Form being answered", formalDefinition="Indicates the Questionnaire resource that defines the form for which answers are being provided." ) - protected Reference questionnaire; - - /** - * The actual object that is the target of the reference (Indicates the Questionnaire resource that defines the form for which answers are being provided.) - */ - protected Questionnaire questionnaireTarget; - - /** - * The lifecycle status of the questionnaire answers as a whole. - */ - @Child(name = "status", type = {CodeType.class}, order=2, min=1, max=1) - @Description(shortDefinition="in-progress | completed | amended", formalDefinition="The lifecycle status of the questionnaire answers as a whole." ) - protected Enumeration status; - - /** - * The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information. - */ - @Child(name = "subject", type = {}, order=3, min=0, max=1) - @Description(shortDefinition="The subject of the questions", formalDefinition="The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information." ) - protected Reference subject; - - /** - * The actual object that is the target of the reference (The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information.) - */ - protected Resource subjectTarget; - - /** - * Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system. - */ - @Child(name = "author", type = {Device.class, Practitioner.class, Patient.class, RelatedPerson.class}, order=4, min=0, max=1) - @Description(shortDefinition="Person who received and recorded the answers", formalDefinition="Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system." ) - protected Reference author; - - /** - * The actual object that is the target of the reference (Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system.) - */ - protected Resource authorTarget; - - /** - * The date and/or time that this version of the questionnaire answers was authored. - */ - @Child(name = "authored", type = {DateTimeType.class}, order=5, min=0, max=1) - @Description(shortDefinition="Date this version was authored", formalDefinition="The date and/or time that this version of the questionnaire answers was authored." ) - protected DateTimeType authored; - - /** - * The person who answered the questions about the subject. - */ - @Child(name = "source", type = {Patient.class, Practitioner.class, RelatedPerson.class}, order=6, min=0, max=1) - @Description(shortDefinition="The person who answered the questions", formalDefinition="The person who answered the questions about the subject." ) - protected Reference source; - - /** - * The actual object that is the target of the reference (The person who answered the questions about the subject.) - */ - protected Resource sourceTarget; - - /** - * Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers. - */ - @Child(name = "encounter", type = {Encounter.class}, order=7, min=0, max=1) - @Description(shortDefinition="Primary encounter during which the answers were collected", formalDefinition="Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers." ) - protected Reference encounter; - - /** - * The actual object that is the target of the reference (Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers.) - */ - protected Encounter encounterTarget; - - /** - * A group of questions to a possibly similarly grouped set of questions in the questionnaire answers. - */ - @Child(name = "group", type = {}, order=8, min=0, max=1) - @Description(shortDefinition="Grouped questions", formalDefinition="A group of questions to a possibly similarly grouped set of questions in the questionnaire answers." ) - protected GroupComponent group; - - private static final long serialVersionUID = -949684393L; - - /* - * Constructor - */ - public QuestionnaireAnswers() { - super(); - } - - /* - * Constructor - */ - public QuestionnaireAnswers(Enumeration status) { - super(); - this.status = status; - } - - /** - * @return {@link #identifier} (A business identifier assigned to a particular completed (or partially completed) questionnaire.) - */ - public Identifier getIdentifier() { - if (this.identifier == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.identifier"); - else if (Configuration.doAutoCreate()) - this.identifier = new Identifier(); // cc - return this.identifier; - } - - public boolean hasIdentifier() { - return this.identifier != null && !this.identifier.isEmpty(); - } - - /** - * @param value {@link #identifier} (A business identifier assigned to a particular completed (or partially completed) questionnaire.) - */ - public QuestionnaireAnswers setIdentifier(Identifier value) { - this.identifier = value; - return this; - } - - /** - * @return {@link #questionnaire} (Indicates the Questionnaire resource that defines the form for which answers are being provided.) - */ - public Reference getQuestionnaire() { - if (this.questionnaire == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.questionnaire"); - else if (Configuration.doAutoCreate()) - this.questionnaire = new Reference(); // cc - return this.questionnaire; - } - - public boolean hasQuestionnaire() { - return this.questionnaire != null && !this.questionnaire.isEmpty(); - } - - /** - * @param value {@link #questionnaire} (Indicates the Questionnaire resource that defines the form for which answers are being provided.) - */ - public QuestionnaireAnswers setQuestionnaire(Reference value) { - this.questionnaire = value; - return this; - } - - /** - * @return {@link #questionnaire} The actual object that is the target of the reference. The reference library doesn't populate this, but you can use it to hold the resource if you resolve it. (Indicates the Questionnaire resource that defines the form for which answers are being provided.) - */ - public Questionnaire getQuestionnaireTarget() { - if (this.questionnaireTarget == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.questionnaire"); - else if (Configuration.doAutoCreate()) - this.questionnaireTarget = new Questionnaire(); // aa - return this.questionnaireTarget; - } - - /** - * @param value {@link #questionnaire} The actual object that is the target of the reference. The reference library doesn't use these, but you can use it to hold the resource if you resolve it. (Indicates the Questionnaire resource that defines the form for which answers are being provided.) - */ - public QuestionnaireAnswers setQuestionnaireTarget(Questionnaire value) { - this.questionnaireTarget = value; - return this; - } - - /** - * @return {@link #status} (The lifecycle status of the questionnaire answers as a whole.). This is the underlying object with id, value and extensions. The accessor "getStatus" gives direct access to the value - */ - public Enumeration getStatusElement() { - if (this.status == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.status"); - else if (Configuration.doAutoCreate()) - this.status = new Enumeration(new QuestionnaireAnswersStatusEnumFactory()); // bb - return this.status; - } - - public boolean hasStatusElement() { - return this.status != null && !this.status.isEmpty(); - } - - public boolean hasStatus() { - return this.status != null && !this.status.isEmpty(); - } - - /** - * @param value {@link #status} (The lifecycle status of the questionnaire answers as a whole.). This is the underlying object with id, value and extensions. The accessor "getStatus" gives direct access to the value - */ - public QuestionnaireAnswers setStatusElement(Enumeration value) { - this.status = value; - return this; - } - - /** - * @return The lifecycle status of the questionnaire answers as a whole. - */ - public QuestionnaireAnswersStatus getStatus() { - return this.status == null ? null : this.status.getValue(); - } - - /** - * @param value The lifecycle status of the questionnaire answers as a whole. - */ - public QuestionnaireAnswers setStatus(QuestionnaireAnswersStatus value) { - if (this.status == null) - this.status = new Enumeration(new QuestionnaireAnswersStatusEnumFactory()); - this.status.setValue(value); - return this; - } - - /** - * @return {@link #subject} (The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information.) - */ - public Reference getSubject() { - if (this.subject == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.subject"); - else if (Configuration.doAutoCreate()) - this.subject = new Reference(); // cc - return this.subject; - } - - public boolean hasSubject() { - return this.subject != null && !this.subject.isEmpty(); - } - - /** - * @param value {@link #subject} (The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information.) - */ - public QuestionnaireAnswers setSubject(Reference value) { - this.subject = value; - return this; - } - - /** - * @return {@link #subject} The actual object that is the target of the reference. The reference library doesn't populate this, but you can use it to hold the resource if you resolve it. (The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information.) - */ - public Resource getSubjectTarget() { - return this.subjectTarget; - } - - /** - * @param value {@link #subject} The actual object that is the target of the reference. The reference library doesn't use these, but you can use it to hold the resource if you resolve it. (The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information.) - */ - public QuestionnaireAnswers setSubjectTarget(Resource value) { - this.subjectTarget = value; - return this; - } - - /** - * @return {@link #author} (Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system.) - */ - public Reference getAuthor() { - if (this.author == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.author"); - else if (Configuration.doAutoCreate()) - this.author = new Reference(); // cc - return this.author; - } - - public boolean hasAuthor() { - return this.author != null && !this.author.isEmpty(); - } - - /** - * @param value {@link #author} (Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system.) - */ - public QuestionnaireAnswers setAuthor(Reference value) { - this.author = value; - return this; - } - - /** - * @return {@link #author} The actual object that is the target of the reference. The reference library doesn't populate this, but you can use it to hold the resource if you resolve it. (Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system.) - */ - public Resource getAuthorTarget() { - return this.authorTarget; - } - - /** - * @param value {@link #author} The actual object that is the target of the reference. The reference library doesn't use these, but you can use it to hold the resource if you resolve it. (Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system.) - */ - public QuestionnaireAnswers setAuthorTarget(Resource value) { - this.authorTarget = value; - return this; - } - - /** - * @return {@link #authored} (The date and/or time that this version of the questionnaire answers was authored.). This is the underlying object with id, value and extensions. The accessor "getAuthored" gives direct access to the value - */ - public DateTimeType getAuthoredElement() { - if (this.authored == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.authored"); - else if (Configuration.doAutoCreate()) - this.authored = new DateTimeType(); // bb - return this.authored; - } - - public boolean hasAuthoredElement() { - return this.authored != null && !this.authored.isEmpty(); - } - - public boolean hasAuthored() { - return this.authored != null && !this.authored.isEmpty(); - } - - /** - * @param value {@link #authored} (The date and/or time that this version of the questionnaire answers was authored.). This is the underlying object with id, value and extensions. The accessor "getAuthored" gives direct access to the value - */ - public QuestionnaireAnswers setAuthoredElement(DateTimeType value) { - this.authored = value; - return this; - } - - /** - * @return The date and/or time that this version of the questionnaire answers was authored. - */ - public Date getAuthored() { - return this.authored == null ? null : this.authored.getValue(); - } - - /** - * @param value The date and/or time that this version of the questionnaire answers was authored. - */ - public QuestionnaireAnswers setAuthored(Date value) { - if (value == null) - this.authored = null; - else { - if (this.authored == null) - this.authored = new DateTimeType(); - this.authored.setValue(value); - } - return this; - } - - /** - * @return {@link #source} (The person who answered the questions about the subject.) - */ - public Reference getSource() { - if (this.source == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.source"); - else if (Configuration.doAutoCreate()) - this.source = new Reference(); // cc - return this.source; - } - - public boolean hasSource() { - return this.source != null && !this.source.isEmpty(); - } - - /** - * @param value {@link #source} (The person who answered the questions about the subject.) - */ - public QuestionnaireAnswers setSource(Reference value) { - this.source = value; - return this; - } - - /** - * @return {@link #source} The actual object that is the target of the reference. The reference library doesn't populate this, but you can use it to hold the resource if you resolve it. (The person who answered the questions about the subject.) - */ - public Resource getSourceTarget() { - return this.sourceTarget; - } - - /** - * @param value {@link #source} The actual object that is the target of the reference. The reference library doesn't use these, but you can use it to hold the resource if you resolve it. (The person who answered the questions about the subject.) - */ - public QuestionnaireAnswers setSourceTarget(Resource value) { - this.sourceTarget = value; - return this; - } - - /** - * @return {@link #encounter} (Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers.) - */ - public Reference getEncounter() { - if (this.encounter == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.encounter"); - else if (Configuration.doAutoCreate()) - this.encounter = new Reference(); // cc - return this.encounter; - } - - public boolean hasEncounter() { - return this.encounter != null && !this.encounter.isEmpty(); - } - - /** - * @param value {@link #encounter} (Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers.) - */ - public QuestionnaireAnswers setEncounter(Reference value) { - this.encounter = value; - return this; - } - - /** - * @return {@link #encounter} The actual object that is the target of the reference. The reference library doesn't populate this, but you can use it to hold the resource if you resolve it. (Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers.) - */ - public Encounter getEncounterTarget() { - if (this.encounterTarget == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.encounter"); - else if (Configuration.doAutoCreate()) - this.encounterTarget = new Encounter(); // aa - return this.encounterTarget; - } - - /** - * @param value {@link #encounter} The actual object that is the target of the reference. The reference library doesn't use these, but you can use it to hold the resource if you resolve it. (Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers.) - */ - public QuestionnaireAnswers setEncounterTarget(Encounter value) { - this.encounterTarget = value; - return this; - } - - /** - * @return {@link #group} (A group of questions to a possibly similarly grouped set of questions in the questionnaire answers.) - */ - public GroupComponent getGroup() { - if (this.group == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create QuestionnaireAnswers.group"); - else if (Configuration.doAutoCreate()) - this.group = new GroupComponent(); // cc - return this.group; - } - - public boolean hasGroup() { - return this.group != null && !this.group.isEmpty(); - } - - /** - * @param value {@link #group} (A group of questions to a possibly similarly grouped set of questions in the questionnaire answers.) - */ - public QuestionnaireAnswers setGroup(GroupComponent value) { - this.group = value; - return this; - } - - protected void listChildren(List childrenList) { - super.listChildren(childrenList); - childrenList.add(new Property("identifier", "Identifier", "A business identifier assigned to a particular completed (or partially completed) questionnaire.", 0, java.lang.Integer.MAX_VALUE, identifier)); - childrenList.add(new Property("questionnaire", "Reference(Questionnaire)", "Indicates the Questionnaire resource that defines the form for which answers are being provided.", 0, java.lang.Integer.MAX_VALUE, questionnaire)); - childrenList.add(new Property("status", "code", "The lifecycle status of the questionnaire answers as a whole.", 0, java.lang.Integer.MAX_VALUE, status)); - childrenList.add(new Property("subject", "Reference(Any)", "The subject of the questionnaire answers. This could be a patient, organization, practitioner, device, etc. This is who/what the answers apply to, but is not necessarily the source of information.", 0, java.lang.Integer.MAX_VALUE, subject)); - childrenList.add(new Property("author", "Reference(Device|Practitioner|Patient|RelatedPerson)", "Person who received the answers to the questions in the QuestionnaireAnswers and recorded them in the system.", 0, java.lang.Integer.MAX_VALUE, author)); - childrenList.add(new Property("authored", "dateTime", "The date and/or time that this version of the questionnaire answers was authored.", 0, java.lang.Integer.MAX_VALUE, authored)); - childrenList.add(new Property("source", "Reference(Patient|Practitioner|RelatedPerson)", "The person who answered the questions about the subject.", 0, java.lang.Integer.MAX_VALUE, source)); - childrenList.add(new Property("encounter", "Reference(Encounter)", "Encounter during which this set of questionnaire answers were collected. When there were multiple encounters, this is the one considered most relevant to the context of the answers.", 0, java.lang.Integer.MAX_VALUE, encounter)); - childrenList.add(new Property("group", "", "A group of questions to a possibly similarly grouped set of questions in the questionnaire answers.", 0, java.lang.Integer.MAX_VALUE, group)); - } - - public QuestionnaireAnswers copy() { - QuestionnaireAnswers dst = new QuestionnaireAnswers(); - copyValues(dst); - dst.identifier = identifier == null ? null : identifier.copy(); - dst.questionnaire = questionnaire == null ? null : questionnaire.copy(); - dst.status = status == null ? null : status.copy(); - dst.subject = subject == null ? null : subject.copy(); - dst.author = author == null ? null : author.copy(); - dst.authored = authored == null ? null : authored.copy(); - dst.source = source == null ? null : source.copy(); - dst.encounter = encounter == null ? null : encounter.copy(); - dst.group = group == null ? null : group.copy(); - return dst; - } - - protected QuestionnaireAnswers typedCopy() { - return copy(); - } - - @Override - public boolean equalsDeep(Base other) { - if (!super.equalsDeep(other)) - return false; - if (!(other instanceof QuestionnaireAnswers)) - return false; - QuestionnaireAnswers o = (QuestionnaireAnswers) other; - return compareDeep(identifier, o.identifier, true) && compareDeep(questionnaire, o.questionnaire, true) - && compareDeep(status, o.status, true) && compareDeep(subject, o.subject, true) && compareDeep(author, o.author, true) - && compareDeep(authored, o.authored, true) && compareDeep(source, o.source, true) && compareDeep(encounter, o.encounter, true) - && compareDeep(group, o.group, true); - } - - @Override - public boolean equalsShallow(Base other) { - if (!super.equalsShallow(other)) - return false; - if (!(other instanceof QuestionnaireAnswers)) - return false; - QuestionnaireAnswers o = (QuestionnaireAnswers) other; - return compareValues(status, o.status, true) && compareValues(authored, o.authored, true); - } - - public boolean isEmpty() { - return super.isEmpty() && (identifier == null || identifier.isEmpty()) && (questionnaire == null || questionnaire.isEmpty()) - && (status == null || status.isEmpty()) && (subject == null || subject.isEmpty()) && (author == null || author.isEmpty()) - && (authored == null || authored.isEmpty()) && (source == null || source.isEmpty()) && (encounter == null || encounter.isEmpty()) - && (group == null || group.isEmpty()); - } - - @Override - public ResourceType getResourceType() { - return ResourceType.QuestionnaireAnswers; - } - - @SearchParamDefinition(name="authored", path="QuestionnaireAnswers.authored", description="When the questionnaire was authored", type="date" ) - public static final String SP_AUTHORED = "authored"; - @SearchParamDefinition(name="questionnaire", path="QuestionnaireAnswers.questionnaire", description="The questionnaire the answers are provided for", type="reference" ) - public static final String SP_QUESTIONNAIRE = "questionnaire"; - @SearchParamDefinition(name="subject", path="QuestionnaireAnswers.subject", description="The subject of the questionnaire", type="reference" ) - public static final String SP_SUBJECT = "subject"; - @SearchParamDefinition(name="author", path="QuestionnaireAnswers.author", description="The author of the questionnaire", type="reference" ) - public static final String SP_AUTHOR = "author"; - @SearchParamDefinition(name="patient", path="QuestionnaireAnswers.subject", description="The patient that is the subject of the questionnaire", type="reference" ) - public static final String SP_PATIENT = "patient"; - @SearchParamDefinition(name="encounter", path="QuestionnaireAnswers.encounter", description="Encounter during which questionnaire was authored", type="reference" ) - public static final String SP_ENCOUNTER = "encounter"; - @SearchParamDefinition(name="source", path="QuestionnaireAnswers.source", description="The person who answered the questions", type="reference" ) - public static final String SP_SOURCE = "source"; - @SearchParamDefinition(name="status", path="QuestionnaireAnswers.status", description="The status of the questionnaire answers", type="token" ) - public static final String SP_STATUS = "status"; - -} - diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 60f40dfd65e..f53ef987481 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -96,6 +96,10 @@ JPA server now implements the $validate-code operation + + HAPI-FHIR now has support for _summary and _elements parameters, in server, client, + and JPA server. + diff --git a/src/site/xdoc/doc_rest_operations.xml b/src/site/xdoc/doc_rest_operations.xml index 522c239ea0a..80b02082698 100644 --- a/src/site/xdoc/doc_rest_operations.xml +++ b/src/site/xdoc/doc_rest_operations.xml @@ -1525,6 +1525,25 @@ + + + + + + +
+ + The _summary and _elements parameters are + automatically handled by the server, so no coding is required to make this + work. If you wish to add parameters to manually handle these fields however, + the following example shows how to access these. + + + + + + +
diff --git a/src/site/xdoc/doc_rest_server.xml b/src/site/xdoc/doc_rest_server.xml index e61329c98aa..4d9bb184f01 100644 --- a/src/site/xdoc/doc_rest_server.xml +++ b/src/site/xdoc/doc_rest_server.xml @@ -361,20 +361,66 @@ - - +

- There are different ways of - generating narratives for use on your server. HAPI's Server - also provides a non-standard parameter called _narrative which can be used to - control narrative behavour. If you add a parameter to any server (or annotation client) method - with a type of NarrativeModeEnum, the value will be populated with the value - of this URL parameter. + FHIR allows for the a number of special behaviours where only certain + portions of resources are returned, instead of the entire resource body. + These behaviours are automatically supported in HAPI (as of HAPI 1.2) + and no additional effort needs to be taken.

+

+ The following behaviours are automatically supported by the HAPI server: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescription
_summary=true + Resources will be returned with any elements not marked as summary elements + omitted. +
_summary=text + Only the narrative portion of returned resources will be returned. For a read/vread + operation, the narrative will be served with a content type of text/html. + for other operations, a Bundle will be returned but resources will only include + the text element. +
_summary=data + The narrative (text) portion of the resource will be omitted. +
_summary=count + For a search, only Bundle.count will be returned. +
_elements=[element names] + Only the given top level elements of returned resources will be returned, e.g for + a Patient search: _elements=name,contact +