001package ca.uhn.fhir.rest.method;
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.lang.reflect.Method;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.HashSet;
027import java.util.List;
028
029import org.hl7.fhir.instance.model.api.IBaseResource;
030
031import ca.uhn.fhir.context.FhirContext;
032import ca.uhn.fhir.context.RuntimeSearchParam;
033import ca.uhn.fhir.model.valueset.BundleTypeEnum;
034import ca.uhn.fhir.rest.api.RequestTypeEnum;
035import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
036import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
037import ca.uhn.fhir.rest.server.Constants;
038import ca.uhn.fhir.rest.server.IBundleProvider;
039import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
040import ca.uhn.fhir.rest.server.IRestfulServer;
041import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
042import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
043
044public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBinding {
045
046        private IDynamicSearchResourceProvider myProvider;
047        private List<RuntimeSearchParam> mySearchParameters;
048        private HashSet<String> myParamNames;
049        private Integer myIdParamIndex;
050
051        public DynamicSearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Method theMethod, FhirContext theConetxt, IDynamicSearchResourceProvider theProvider) {
052                super(theReturnResourceType, theMethod, theConetxt, theProvider);
053
054                myProvider = theProvider;
055                mySearchParameters = myProvider.getSearchParameters();
056
057                myParamNames = new HashSet<String>();
058                for (RuntimeSearchParam next : mySearchParameters) {
059                        myParamNames.add(next.getName());
060                }
061
062                myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
063
064        }
065
066        @Override
067        protected BundleTypeEnum getResponseBundleType() {
068                return BundleTypeEnum.SEARCHSET;
069        }
070
071
072        @Override
073        public List<IParameter> getParameters() {
074                List<IParameter> retVal = new ArrayList<IParameter>(super.getParameters());
075                
076                for (RuntimeSearchParam next : mySearchParameters) {
077                        // TODO: what is this?
078                }
079                
080                return retVal;
081        }
082
083        @Override
084        public ReturnTypeEnum getReturnType() {
085                return ReturnTypeEnum.BUNDLE;
086        }
087
088        @Override
089        public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
090                if (myIdParamIndex != null) {
091                        theMethodParams[myIdParamIndex] = theRequest.getId();
092                }
093
094                Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
095                return toResourceList(response);
096        }
097
098        @Override
099        public RestOperationTypeEnum getRestOperationType() {
100                return RestOperationTypeEnum.SEARCH_TYPE;
101        }
102
103        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DynamicSearchMethodBinding.class);
104
105        @Override
106        public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
107                if (!theRequest.getResourceName().equals(getResourceName())) {
108                        ourLog.trace("Method {} doesn't match because resource name {} != {}", new Object[] { getMethod().getName(), theRequest.getResourceName(), getResourceName() } );
109                        return false;
110                }
111                if (theRequest.getId() != null && myIdParamIndex == null) {
112                        ourLog.trace("Method {} doesn't match because ID is not null: {}", theRequest.getId());
113                        return false;
114                }
115                if (theRequest.getRequestType() == RequestTypeEnum.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
116                        ourLog.trace("Method {} doesn't match because request type is GET but operation is not null: {}", theRequest.getId(), theRequest.getOperation());
117                        return false;
118                }
119                if (theRequest.getRequestType() == RequestTypeEnum.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
120                        ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
121                        return false;
122                }
123                if (theRequest.getRequestType() != RequestTypeEnum.GET && theRequest.getRequestType() != RequestTypeEnum.POST) {
124                        ourLog.trace("Method {} doesn't match because request type is {}", getMethod());
125                        return false;
126                }
127                if (theRequest.getCompartmentName() != null) {
128                        ourLog.trace("Method {} doesn't match because it is for compartment {}", new Object[] { getMethod(), theRequest.getCompartmentName() });
129                        return false;
130                }
131
132                for (String next : theRequest.getParameters().keySet()) {
133                        if (next.charAt(0) == '_') {
134                                continue;
135                        }
136                        String nextQualified = next;
137                        int colonIndex = next.indexOf(':');
138                        int dotIndex = next.indexOf('.');
139                        if (colonIndex != -1 || dotIndex != -1) {
140                                int index;
141                                if (colonIndex != -1 && dotIndex != -1) {
142                                        index = Math.min(colonIndex, dotIndex);
143                                } else {
144                                        index = (colonIndex != -1) ? colonIndex : dotIndex;
145                                }
146                                next = next.substring(0, index);
147                        }
148                        if (!myParamNames.contains(next)) {
149                                ourLog.trace("Method {} doesn't match because has parameter {}", new Object[] { getMethod(), nextQualified });
150                                return false;
151                        }
152                }
153
154                return true;
155        }
156
157        @Override
158        public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
159                // there should be no way to call this....
160                throw new UnsupportedOperationException("Dynamic search methods are only used for server implementations");
161        }
162
163        public Collection<? extends RuntimeSearchParam> getSearchParams() {
164                return mySearchParameters;
165        }
166
167}