001package ca.uhn.fhir.rest.param;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import java.io.Reader;
024import java.lang.reflect.Method;
025import java.lang.reflect.Modifier;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.List;
029import java.util.Map;
030
031import org.hl7.fhir.instance.model.api.IBaseResource;
032
033import ca.uhn.fhir.context.ConfigurationException;
034import ca.uhn.fhir.context.FhirContext;
035import ca.uhn.fhir.context.RuntimeResourceDefinition;
036import ca.uhn.fhir.model.api.Bundle;
037import ca.uhn.fhir.model.api.BundleEntry;
038import ca.uhn.fhir.model.api.IResource;
039import ca.uhn.fhir.parser.IParser;
040import ca.uhn.fhir.rest.annotation.TransactionParam;
041import ca.uhn.fhir.rest.method.BaseMethodBinding;
042import ca.uhn.fhir.rest.method.IParameter;
043import ca.uhn.fhir.rest.method.RequestDetails;
044import ca.uhn.fhir.rest.server.EncodingEnum;
045import ca.uhn.fhir.rest.server.RestfulServerUtils;
046import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
047import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
048
049public class TransactionParameter implements IParameter {
050
051        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionParameter.class);   
052        private FhirContext myContext;
053        private ParamStyle myParamStyle;
054        private Class<? extends IBaseResource> myResourceBundleType;
055        
056        public TransactionParameter(FhirContext theContext) {
057                myContext = theContext;
058        }
059
060        private String createParameterTypeError(Method theMethod) {
061                return "Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName() + "> or Bundle";
062        }
063
064        @Override
065        public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
066                if (theOuterCollectionType != null) {
067                        throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + TransactionParam.class.getName() + " but can not be a collection of collections");
068                }
069                if (theParameterType.equals(Bundle.class)) {
070                        myParamStyle = ParamStyle.DSTU1_BUNDLE;
071                        if (theInnerCollectionType != null) {
072                                throw new ConfigurationException(createParameterTypeError(theMethod));
073                        }
074                } else if (Modifier.isInterface(theParameterType.getModifiers()) == false && IBaseResource.class.isAssignableFrom(theParameterType)) {
075                        @SuppressWarnings("unchecked")
076                        Class<? extends IBaseResource> parameterType = (Class<? extends IBaseResource>) theParameterType;
077                        RuntimeResourceDefinition def = myContext.getResourceDefinition(parameterType);
078                        if ("Bundle".equals(def.getName())) {
079                                myParamStyle = ParamStyle.RESOURCE_BUNDLE;
080                                myResourceBundleType = parameterType;
081                        } else {
082                                throw new ConfigurationException(createParameterTypeError(theMethod));
083                        }
084                } else {
085                        if (theInnerCollectionType.equals(List.class) == false) {
086                                throw new ConfigurationException(createParameterTypeError(theMethod));
087                        }
088                        if (theParameterType.equals(IResource.class) == false) {
089                                throw new ConfigurationException(createParameterTypeError(theMethod));
090                        }
091                        myParamStyle = ParamStyle.RESOURCE_LIST;
092                }
093        }
094
095        @Override
096        public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
097                // nothing
098
099        }
100        
101        @Override
102        public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
103                // TODO: don't use a default encoding, just fail!
104                EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
105
106                IParser parser = encoding.newParser(theRequest.getServer().getFhirContext());
107
108                Reader reader;
109                reader = ResourceParameter.createRequestReader(theRequest);
110
111                switch (myParamStyle) {
112                        case DSTU1_BUNDLE: {
113                                Bundle bundle;
114                                bundle = parser.parseBundle(reader);
115                                return bundle;
116                        }
117                        case RESOURCE_LIST: {
118                                Bundle bundle = parser.parseBundle(reader);
119                                ArrayList<IResource> resourceList = new ArrayList<IResource>();
120                                for (BundleEntry next : bundle.getEntries()) {
121                                        if (next.getResource() != null) {
122                                                resourceList.add(next.getResource());
123                                        }
124                                }
125                                return resourceList;
126                        }
127                        case RESOURCE_BUNDLE:
128                                return parser.parseResource(myResourceBundleType, reader);
129                }
130
131                throw new IllegalStateException("Unknown type: " + myParamStyle); // should not happen
132        }
133
134        public ParamStyle getParamStyle() {
135                return myParamStyle;
136        }
137
138        public enum ParamStyle {
139                /** Old style bundle (defined in hapi-fhir-base) */
140                DSTU1_BUNDLE,
141                /** New style bundle (defined in hapi-fhir-structures-* as a resource definition itself */
142                RESOURCE_BUNDLE,
143                /** List of resources */
144                RESOURCE_LIST
145        }
146
147}