Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
ca.uhn.fhir.rest.method.OperationParameter Maven / Gradle / Ivy
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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 java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IAccessor;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.IRuntimeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeChildPrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.param.BaseAndListParam;
import ca.uhn.fhir.rest.param.CollectionBinder;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ParametersUtil;
import ca.uhn.fhir.util.ReflectionUtil;
public class OperationParameter implements IParameter {
@SuppressWarnings("unchecked")
private static final Class extends IQueryParameterType>[] COMPOSITE_TYPES = new Class[0];
static final String REQUEST_CONTENTS_USERDATA_KEY = OperationParam.class.getName() + "_PARSED_RESOURCE";
private boolean myAllowGet;
private final FhirContext myContext;
private IOperationParamConverter myConverter;
@SuppressWarnings("rawtypes")
private Class extends Collection> myInnerCollectionType;
private int myMax;
private int myMin;
private final String myName;
private final String myOperationName;
private Class> myParameterType;
private String myParamType;
private SearchParameter mySearchParameterBinding;
public OperationParameter(FhirContext theCtx, String theOperationName, OperationParam theOperationParam) {
this(theCtx, theOperationName, theOperationParam.name(), theOperationParam.min(), theOperationParam.max());
}
OperationParameter(FhirContext theCtx, String theOperationName, String theParameterName, int theMin, int theMax) {
myOperationName = theOperationName;
myName = theParameterName;
myMin = theMin;
myMax = theMax;
myContext = theCtx;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addValueToList(List matchingParamValues, Object values) {
if (values != null) {
if (BaseAndListParam.class.isAssignableFrom(myParameterType) && matchingParamValues.size() > 0) {
BaseAndListParam existing = (BaseAndListParam>) matchingParamValues.get(0);
BaseAndListParam> newAndList = (BaseAndListParam>) values;
for (IQueryParameterOr nextAnd : newAndList.getValuesAsQueryTokens()) {
existing.addAnd(nextAnd);
}
} else {
matchingParamValues.add(values);
}
}
}
protected FhirContext getContext() {
return myContext;
}
public int getMax() {
return myMax;
}
public int getMin() {
return myMin;
}
public String getName() {
return myName;
}
public String getParamType() {
return myParamType;
}
public String getSearchParamType() {
if (mySearchParameterBinding != null) {
return mySearchParameterBinding.getParamType().getCode();
} else {
return null;
}
}
@SuppressWarnings("unchecked")
@Override
public void initializeTypes(Method theMethod, Class extends Collection>> theOuterCollectionType, Class extends Collection>> theInnerCollectionType, Class> theParameterType) {
if (getContext().getVersion().getVersion().isRi()) {
if (IDatatype.class.isAssignableFrom(theParameterType)) {
throw new ConfigurationException("Incorrect use of type " + theParameterType.getSimpleName() + " as parameter type for method when context is for version " + getContext().getVersion().getVersion().name() + " in method: " + theMethod.toString());
}
}
myParameterType = theParameterType;
if (theInnerCollectionType != null) {
myInnerCollectionType = CollectionBinder.getInstantiableCollectionType(theInnerCollectionType, myName);
if (myMax == OperationParam.MAX_DEFAULT) {
myMax = OperationParam.MAX_UNLIMITED;
}
} else if (IQueryParameterAnd.class.isAssignableFrom(myParameterType)) {
if (myMax == OperationParam.MAX_DEFAULT) {
myMax = OperationParam.MAX_UNLIMITED;
}
} else {
if (myMax == OperationParam.MAX_DEFAULT) {
myMax = 1;
}
}
boolean typeIsConcrete = !myParameterType.isInterface() && !Modifier.isAbstract(myParameterType.getModifiers());
//@formatter:off
boolean isSearchParam =
IQueryParameterType.class.isAssignableFrom(myParameterType) ||
IQueryParameterOr.class.isAssignableFrom(myParameterType) ||
IQueryParameterAnd.class.isAssignableFrom(myParameterType);
//@formatter:off
/*
* Note: We say here !IBase.class.isAssignableFrom because a bunch of DSTU1/2 datatypes also
* extend this interface. I'm not sure if they should in the end.. but they do, so we
* exclude them.
*/
isSearchParam &= typeIsConcrete && !IBase.class.isAssignableFrom(myParameterType);
myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType) || String.class.equals(myParameterType) || isSearchParam || ValidationModeEnum.class.equals(myParameterType);
/*
* The parameter can be of type string for validation methods - This is a bit weird. See ValidateDstu2Test. We
* should probably clean this up..
*/
if (!myParameterType.equals(IBase.class) && !myParameterType.equals(String.class)) {
if (IBaseResource.class.isAssignableFrom(myParameterType) && myParameterType.isInterface()) {
myParamType = "Resource";
} else if (DateRangeParam.class.isAssignableFrom(myParameterType)) {
myParamType = "date";
myMax = 2;
myAllowGet = true;
} else if (myParameterType.equals(ValidationModeEnum.class)) {
myParamType = "code";
} else if (IBase.class.isAssignableFrom(myParameterType) && typeIsConcrete) {
myParamType = myContext.getElementDefinition((Class extends IBase>) myParameterType).getName();
} else if (isSearchParam) {
myParamType = "string";
mySearchParameterBinding = new SearchParameter(myName, myMin > 0);
mySearchParameterBinding.setCompositeTypes(COMPOSITE_TYPES);
mySearchParameterBinding.setType(myContext, theParameterType, theInnerCollectionType, theOuterCollectionType);
myConverter = new OperationParamConverter();
} else {
throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName());
}
}
}
public OperationParameter setConverter(IOperationParamConverter theConverter) {
myConverter = theConverter;
return this;
}
private void throwWrongParamType(Object nextValue) {
throw new InvalidRequestException("Request has parameter " + myName + " of type " + nextValue.getClass().getSimpleName() + " but method expects type " + myParameterType.getSimpleName());
}
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
assert theTargetResource != null;
Object sourceClientArgument = theSourceClientArgument;
if (sourceClientArgument == null) {
return;
}
if (myConverter != null) {
sourceClientArgument = myConverter.outgoingClient(sourceClientArgument);
}
ParametersUtil.addParameterToParameters(theContext, theTargetResource, sourceClientArgument, myName);
}
@SuppressWarnings("unchecked")
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding> theMethodBinding) throws InternalErrorException, InvalidRequestException {
List matchingParamValues = new ArrayList();
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
translateQueryParametersIntoServerArgumentForGet(theRequest, matchingParamValues);
} else {
translateQueryParametersIntoServerArgumentForPost(theRequest, matchingParamValues);
}
if (matchingParamValues.isEmpty()) {
return null;
}
if (myInnerCollectionType == null) {
return matchingParamValues.get(0);
}
Collection retVal = ReflectionUtil.newInstance(myInnerCollectionType);
retVal.addAll(matchingParamValues);
return retVal;
}
private void translateQueryParametersIntoServerArgumentForGet(RequestDetails theRequest, List matchingParamValues) {
if (mySearchParameterBinding != null) {
List params = new ArrayList();
String nameWithQualifierColon = myName + ":";
for (String nextParamName : theRequest.getParameters().keySet()) {
String qualifier;
if (nextParamName.equals(myName)) {
qualifier = null;
} else if (nextParamName.startsWith(nameWithQualifierColon)) {
qualifier = nextParamName.substring(nextParamName.indexOf(':'));
} else {
// This is some other parameter, not the one bound by this instance
continue;
}
String[] values = theRequest.getParameters().get(nextParamName);
if (values != null) {
for (String nextValue : values) {
params.add(QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, nextValue));
}
}
}
if (!params.isEmpty()) {
for (QualifiedParamList next : params) {
Object values = mySearchParameterBinding.parse(myContext, Collections.singletonList(next));
addValueToList(matchingParamValues, values);
}
}
} else {
String[] paramValues = theRequest.getParameters().get(myName);
if (paramValues != null && paramValues.length > 0) {
if (myAllowGet) {
if (DateRangeParam.class.isAssignableFrom(myParameterType)) {
List parameters = new ArrayList();
parameters.add(QualifiedParamList.singleton(paramValues[0]));
if (paramValues.length > 1) {
parameters.add(QualifiedParamList.singleton(paramValues[1]));
}
DateRangeParam dateRangeParam = new DateRangeParam();
FhirContext ctx = theRequest.getServer().getFhirContext();
dateRangeParam.setValuesAsQueryTokens(ctx, myName, parameters);
matchingParamValues.add(dateRangeParam);
} else if (String.class.isAssignableFrom(myParameterType)) {
for (String next : paramValues) {
matchingParamValues.add(next);
}
} else if (ValidationModeEnum.class.equals(myParameterType)) {
if (isNotBlank(paramValues[0])) {
ValidationModeEnum validationMode = ValidationModeEnum.forCode(paramValues[0]);
if (validationMode != null) {
matchingParamValues.add(validationMode);
} else {
throwInvalidMode(paramValues[0]);
}
}
} else {
for (String nextValue : paramValues) {
FhirContext ctx = theRequest.getServer().getFhirContext();
RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition((Class extends IBase>) myParameterType);
IPrimitiveType> instance = def.newInstance();
instance.setValueAsString(nextValue);
matchingParamValues.add(instance);
}
}
} else {
HapiLocalizer localizer = theRequest.getServer().getFhirContext().getLocalizer();
String msg = localizer.getMessage(OperationParameter.class, "urlParamNotPrimitive", myOperationName, myName);
throw new MethodNotAllowedException(msg, RequestTypeEnum.POST);
}
}
}
}
private void translateQueryParametersIntoServerArgumentForPost(RequestDetails theRequest, List matchingParamValues) {
IBaseResource requestContents = (IBaseResource) theRequest.getUserData().get(REQUEST_CONTENTS_USERDATA_KEY);
RuntimeResourceDefinition def = myContext.getResourceDefinition(requestContents);
if (def.getName().equals("Parameters")) {
BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
BaseRuntimeElementCompositeDefinition> paramChildElem = (BaseRuntimeElementCompositeDefinition>) paramChild.getChildByName("parameter");
RuntimeChildPrimitiveDatatypeDefinition nameChild = (RuntimeChildPrimitiveDatatypeDefinition) paramChildElem.getChildByName("name");
BaseRuntimeChildDefinition valueChild = paramChildElem.getChildByName("value[x]");
BaseRuntimeChildDefinition resourceChild = paramChildElem.getChildByName("resource");
IAccessor paramChildAccessor = paramChild.getAccessor();
List values = paramChildAccessor.getValues(requestContents);
for (IBase nextParameter : values) {
List nextNames = nameChild.getAccessor().getValues(nextParameter);
if (nextNames != null && nextNames.size() > 0) {
IPrimitiveType> nextName = (IPrimitiveType>) nextNames.get(0);
if (myName.equals(nextName.getValueAsString())) {
if (myParameterType.isAssignableFrom(nextParameter.getClass())) {
matchingParamValues.add(nextParameter);
} else {
List paramValues = valueChild.getAccessor().getValues(nextParameter);
List paramResources = resourceChild.getAccessor().getValues(nextParameter);
if (paramValues != null && paramValues.size() > 0) {
tryToAddValues(paramValues, matchingParamValues);
} else if (paramResources != null && paramResources.size() > 0) {
tryToAddValues(paramResources, matchingParamValues);
}
}
}
}
}
} else {
if (myParameterType.isAssignableFrom(requestContents.getClass())) {
tryToAddValues(Arrays.asList((IBase) requestContents), matchingParamValues);
}
}
}
@SuppressWarnings("unchecked")
private void tryToAddValues(List theParamValues, List theMatchingParamValues) {
for (Object nextValue : theParamValues) {
if (nextValue == null) {
continue;
}
if (myConverter != null) {
nextValue = myConverter.incomingServer(nextValue);
}
if (!myParameterType.isAssignableFrom(nextValue.getClass())) {
Class extends IBaseDatatype> sourceType = (Class extends IBaseDatatype>) nextValue.getClass();
Class extends IBaseDatatype> targetType = (Class extends IBaseDatatype>) myParameterType;
BaseRuntimeElementDefinition> sourceTypeDef = myContext.getElementDefinition(sourceType);
BaseRuntimeElementDefinition> targetTypeDef = myContext.getElementDefinition(targetType);
if (targetTypeDef instanceof IRuntimeDatatypeDefinition && sourceTypeDef instanceof IRuntimeDatatypeDefinition) {
IRuntimeDatatypeDefinition targetTypeDtDef = (IRuntimeDatatypeDefinition) targetTypeDef;
if (targetTypeDtDef.isProfileOf(sourceType)) {
FhirTerser terser = myContext.newTerser();
IBase newTarget = targetTypeDef.newInstance();
terser.cloneInto((IBase) nextValue, newTarget, true);
theMatchingParamValues.add(newTarget);
continue;
}
}
throwWrongParamType(nextValue);
}
addValueToList(theMatchingParamValues, nextValue);
}
}
public static void throwInvalidMode(String paramValues) {
throw new InvalidRequestException("Invalid mode value: \"" + paramValues + "\"");
}
interface IOperationParamConverter {
Object incomingServer(Object theObject);
Object outgoingClient(Object theObject);
}
class OperationParamConverter implements IOperationParamConverter {
public OperationParamConverter() {
Validate.isTrue(mySearchParameterBinding != null);
}
@Override
public Object incomingServer(Object theObject) {
IPrimitiveType> obj = (IPrimitiveType>) theObject;
List paramList = Collections.singletonList(QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null, obj.getValueAsString()));
return mySearchParameterBinding.parse(myContext, paramList);
}
@Override
public Object outgoingClient(Object theObject) {
IQueryParameterType obj = (IQueryParameterType) theObject;
IPrimitiveType> retVal = (IPrimitiveType>) myContext.getElementDefinition("string").newInstance();
retVal.setValueAsString(obj.getValueAsQueryToken(myContext));
return retVal;
}
}
}