001/*
002 * #%L
003 * HAPI FHIR - Server Framework
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.rest.server.method;
021
022import ca.uhn.fhir.context.ConfigurationException;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.rest.api.Constants;
025import ca.uhn.fhir.rest.api.SummaryEnum;
026import ca.uhn.fhir.rest.api.server.RequestDetails;
027import ca.uhn.fhir.rest.param.binder.CollectionBinder;
028import ca.uhn.fhir.rest.server.ElementsSupportEnum;
029import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
030import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
031import org.apache.commons.lang3.StringUtils;
032
033import java.lang.reflect.Method;
034import java.util.Collection;
035import java.util.HashSet;
036import java.util.Set;
037import java.util.StringTokenizer;
038
039import static org.apache.commons.lang3.StringUtils.isNotBlank;
040
041public class ElementsParameter implements IParameter {
042
043        @SuppressWarnings("rawtypes")
044        private Class<? extends Collection> myInnerCollectionType;
045
046        @Override
047        @SuppressWarnings({"rawtypes", "unchecked"})
048        public Object translateQueryParametersIntoServerArgument(
049                        RequestDetails theRequest, BaseMethodBinding theMethodBinding)
050                        throws InternalErrorException, InvalidRequestException {
051                Set<String> value = getElementsValueOrNull(theRequest, false);
052                if (value == null || value.isEmpty()) {
053                        return null;
054                }
055
056                if (myInnerCollectionType == null) {
057                        return StringUtils.join(value, ',');
058                }
059
060                try {
061                        Collection retVal = myInnerCollectionType.newInstance();
062                        retVal.addAll(value);
063                        return retVal;
064                } catch (InstantiationException e) {
065                        throw new InternalErrorException(Msg.code(413) + "Failed to instantiate " + myInnerCollectionType, e);
066                } catch (IllegalAccessException e) {
067                        throw new InternalErrorException(Msg.code(414) + "Failed to instantiate " + myInnerCollectionType, e);
068                }
069        }
070
071        @Override
072        public void initializeTypes(
073                        Method theMethod,
074                        Class<? extends Collection<?>> theOuterCollectionType,
075                        Class<? extends Collection<?>> theInnerCollectionType,
076                        Class<?> theParameterType) {
077                if (theOuterCollectionType != null) {
078                        throw new ConfigurationException(Msg.code(415) + "Method '" + theMethod.getName() + "' in type '"
079                                        + theMethod.getDeclaringClass().getCanonicalName() + "' is of type " + SummaryEnum.class
080                                        + " but can not be a collection of collections");
081                }
082                if (theInnerCollectionType != null) {
083                        myInnerCollectionType = CollectionBinder.getInstantiableCollectionType(
084                                        theInnerCollectionType, SummaryEnum.class.getSimpleName());
085                }
086        }
087
088        public static Set<String> getElementsValueOrNull(RequestDetails theRequest, boolean theExclude) {
089                boolean standardMode = theRequest.getServer().getElementsSupport() != ElementsSupportEnum.EXTENDED;
090                if (theExclude && standardMode) {
091                        return null;
092                }
093
094                String paramName = Constants.PARAM_ELEMENTS;
095                if (theExclude) {
096                        paramName = Constants.PARAM_ELEMENTS + Constants.PARAM_ELEMENTS_EXCLUDE_MODIFIER;
097                }
098                String[] elementsValues = theRequest.getParameters().get(paramName);
099
100                if (elementsValues != null && elementsValues.length > 0) {
101                        Set<String> retVal = new HashSet<>();
102                        for (String next : elementsValues) {
103                                StringTokenizer tok = new StringTokenizer(next, ",");
104                                while (tok.hasMoreTokens()) {
105                                        String token = tok.nextToken();
106                                        if (isNotBlank(token)) {
107                                                if (token.contains("."))
108                                                        if (standardMode) {
109                                                                continue;
110                                                        }
111                                                retVal.add(token);
112                                        }
113                                }
114                        }
115                        if (retVal.isEmpty()) {
116                                return null;
117                        }
118
119                        return retVal;
120                }
121                return null;
122        }
123}