001package ca.uhn.fhir.rest.method;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 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.Collection;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
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.api.IQueryParameterType;
034import ca.uhn.fhir.rest.param.CompositeOrListParam;
035import ca.uhn.fhir.rest.param.DateOrListParam;
036import ca.uhn.fhir.rest.param.DateParam;
037import ca.uhn.fhir.rest.param.NumberOrListParam;
038import ca.uhn.fhir.rest.param.NumberParam;
039import ca.uhn.fhir.rest.param.QuantityOrListParam;
040import ca.uhn.fhir.rest.param.QuantityParam;
041import ca.uhn.fhir.rest.param.ReferenceOrListParam;
042import ca.uhn.fhir.rest.param.ReferenceParam;
043import ca.uhn.fhir.rest.param.StringOrListParam;
044import ca.uhn.fhir.rest.param.StringParam;
045import ca.uhn.fhir.rest.param.TokenOrListParam;
046import ca.uhn.fhir.rest.param.TokenParam;
047import ca.uhn.fhir.rest.param.UriOrListParam;
048import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
049import ca.uhn.fhir.rest.server.SearchParameterMap;
050import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
051import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
052
053public class DynamicSearchParameter implements IParameter {
054
055        private Map<String, RuntimeSearchParam> myNameToParam = new HashMap<String, RuntimeSearchParam>();
056
057        public DynamicSearchParameter(IDynamicSearchResourceProvider theProvider) {
058                for (RuntimeSearchParam next : theProvider.getSearchParameters()) {
059                        myNameToParam.put(next.getName(), next);
060                }
061        }
062
063        @Override
064        public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
065                throw new UnsupportedOperationException("Dynamic search is not supported in client mode (use fluent client for dynamic-like searches)");
066        }
067
068        @SuppressWarnings("unchecked")
069        @Override
070        public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
071                SearchParameterMap retVal = new SearchParameterMap();
072
073                for (String next : theRequest.getParameters().keySet()) {
074                        String qualifier = null;
075                        String qualifiedParamName = next;
076                        String unqualifiedParamName = next;
077                        RuntimeSearchParam param = myNameToParam.get(next);
078                        if (param == null) {
079                                int colonIndex = next.indexOf(':');
080                                int dotIndex = next.indexOf('.');
081                                if (colonIndex != -1 || dotIndex != -1) {
082                                        int index;
083                                        if (colonIndex != -1 && dotIndex != -1) {
084                                                index = Math.min(colonIndex, dotIndex);
085                                        } else {
086                                                index = (colonIndex != -1) ? colonIndex : dotIndex;
087                                        }
088                                        qualifier = next.substring(index);
089                                        next = next.substring(0, index);
090                                        unqualifiedParamName = next;
091                                        param = myNameToParam.get(next);
092                                }
093                        }
094
095                        if (param != null) {
096
097                                for (String nextValue : theRequest.getParameters().get(qualifiedParamName)) {
098                                        QualifiedParamList paramList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, nextValue);
099
100                                        FhirContext ctx = theRequest.getServer().getFhirContext();
101                                        
102                                        switch (param.getParamType()) {
103                                        case COMPOSITE:
104                                                Class<? extends IQueryParameterType> left = toParamType(param.getCompositeOf().get(0));
105                                                Class<? extends IQueryParameterType> right = toParamType(param.getCompositeOf().get(0));
106                                                @SuppressWarnings({ "rawtypes" })
107                                                CompositeOrListParam compositeOrListParam = new CompositeOrListParam(left, right);
108                                                compositeOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
109                                                retVal.add(next, compositeOrListParam);
110                                                break;
111                                        case DATE:
112                                                DateOrListParam dateOrListParam = new DateOrListParam();
113                                                dateOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
114                                                retVal.add(next, dateOrListParam);
115                                                break;
116                                        case NUMBER:
117                                                NumberOrListParam numberOrListParam = new NumberOrListParam();
118                                                numberOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
119                                                retVal.add(next, numberOrListParam);
120                                                break;
121                                        case QUANTITY:
122                                                QuantityOrListParam quantityOrListParam = new QuantityOrListParam();
123                                                quantityOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
124                                                retVal.add(next, quantityOrListParam);
125                                                break;
126                                        case REFERENCE:
127                                                ReferenceOrListParam referenceOrListParam = new ReferenceOrListParam();
128                                                referenceOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
129                                                retVal.add(next, referenceOrListParam);
130                                                break;
131                                        case STRING:
132                                                StringOrListParam stringOrListParam = new StringOrListParam();
133                                                stringOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
134                                                retVal.add(next, stringOrListParam);
135                                                break;
136                                        case TOKEN:
137                                                TokenOrListParam tokenOrListParam = new TokenOrListParam();
138                                                tokenOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
139                                                retVal.add(next, tokenOrListParam);
140                                                break;
141                                        case URI:
142                                                UriOrListParam uriOrListParam = new UriOrListParam();
143                                                uriOrListParam.setValuesAsQueryTokens(ctx, unqualifiedParamName, paramList);
144                                                retVal.add(next, uriOrListParam);
145                                                break;
146                                        case HAS:
147                                                // Should not happen
148                                                break;
149                                        }
150                                }
151                        }
152                }
153
154                return retVal;
155        }
156
157        private Class<? extends IQueryParameterType> toParamType(RuntimeSearchParam theRuntimeSearchParam) {
158                switch (theRuntimeSearchParam.getParamType()) {
159                case COMPOSITE:
160                        throw new IllegalStateException("Composite subtype");
161                case DATE:
162                        return DateParam.class;
163                case NUMBER:
164                        return NumberParam.class;
165                case QUANTITY:
166                        return QuantityParam.class;
167                case REFERENCE:
168                        return ReferenceParam.class;
169                case STRING:
170                        return StringParam.class;
171                case TOKEN:
172                        return TokenParam.class;
173                default:
174                        throw new IllegalStateException("null type");
175                }
176        }
177
178        @Override
179        public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
180                // nothing
181        }
182
183}