001/*-
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2025 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.interceptor.api;
021
022import com.google.common.collect.ArrayListMultimap;
023import com.google.common.collect.ListMultimap;
024import com.google.common.collect.Multimaps;
025import jakarta.annotation.Nonnull;
026import org.apache.commons.lang3.Validate;
027import org.hl7.fhir.instance.model.api.IBaseResource;
028
029import java.util.Collection;
030import java.util.Collections;
031import java.util.List;
032import java.util.function.Supplier;
033import java.util.stream.Collectors;
034
035public class HookParams {
036
037        private ListMultimap<Class<?>, Object> myParams = ArrayListMultimap.create();
038
039        /**
040         * Constructor
041         */
042        public HookParams() {}
043
044        /**
045         * Constructor
046         */
047        public HookParams(Object... theParams) {
048                for (Object next : theParams) {
049                        add(next);
050                }
051        }
052
053        @SuppressWarnings("unchecked")
054        public <T> HookParams add(@Nonnull T theNext) {
055                Class<T> nextClass = (Class<T>) theNext.getClass();
056                add(nextClass, theNext);
057                return this;
058        }
059
060        public <T> HookParams add(Class<T> theType, T theParam) {
061                return doAdd(theType, theParam);
062        }
063
064        //      /**
065        //       * This is useful for providing a lazy-loaded (generally expensive to create)
066        //       * parameters
067        //       */
068        //      public <T> HookParams addSupplier(Class<T> theType, Supplier<T> theParam) {
069        //              return doAdd(theType, theParam);
070        //      }
071
072        private <T> HookParams doAdd(Class<T> theType, Object theParam) {
073                Validate.isTrue(theType.equals(Supplier.class) == false, "Can not add parameters of type Supplier");
074                myParams.put(theType, theParam);
075                return this;
076        }
077
078        public <T> T get(Class<T> theParamType) {
079                return get(theParamType, 0);
080        }
081
082        @SuppressWarnings("unchecked")
083        public <T> T get(Class<T> theParamType, int theIndex) {
084                List<Object> objects = myParams.get(theParamType);
085                Object retVal = null;
086                if (objects.size() > theIndex) {
087                        retVal = objects.get(theIndex);
088                }
089
090                retVal = unwrapValue(retVal);
091
092                return (T) retVal;
093        }
094
095        private Object unwrapValue(Object theValue) {
096                if (theValue instanceof Supplier) {
097                        theValue = ((Supplier) theValue).get();
098                }
099                return theValue;
100        }
101
102        /**
103         * Returns an unmodifiable multimap of the params, where the
104         * key is the param type and the value is the actual instance
105         */
106        public ListMultimap<Class<?>, Object> getParamsForType() {
107                ArrayListMultimap<Class<?>, Object> retVal = ArrayListMultimap.create();
108                myParams.entries().forEach(entry -> retVal.put(entry.getKey(), unwrapValue(entry.getValue())));
109                return Multimaps.unmodifiableListMultimap(retVal);
110        }
111
112        public Collection<Object> values() {
113                return Collections.unmodifiableCollection(myParams.values()).stream()
114                                .map(t -> unwrapValue(t))
115                                .collect(Collectors.toList());
116        }
117
118        @SuppressWarnings("unchecked")
119        public <T> HookParams addIfMatchesType(Class<T> theType, Object theParam) {
120                if (theParam == null) {
121                        add(theType, null);
122                } else {
123                        if (theType.isAssignableFrom(theParam.getClass())) {
124                                T param = (T) theParam;
125                                add(theType, param);
126                        } else {
127                                add(theType, null);
128                        }
129                }
130                return this;
131        }
132
133        @Override
134        public String toString() {
135                StringBuilder b = new StringBuilder();
136                myParams.forEach((key, value) -> {
137                        b.append("  ")
138                                        .append(key.getSimpleName())
139                                        .append(": ")
140                                        .append(valueAsStringForLog(value))
141                                        .append("\n");
142                });
143                return b.toString();
144        }
145
146        private static String valueAsStringForLog(Object theValue) {
147                if (theValue instanceof IBaseResource) {
148                        return ((IBaseResource) theValue).getIdElement().toString();
149                }
150                return theValue.toString();
151        }
152}