001package ca.uhn.fhir.util;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2019 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 */
022import java.lang.reflect.*;
023import java.util.LinkedHashSet;
024import java.util.List;
025import java.util.concurrent.ConcurrentHashMap;
026
027import org.apache.commons.lang3.Validate;
028
029import ca.uhn.fhir.context.ConfigurationException;
030import ca.uhn.fhir.context.support.IContextValidationSupport;
031
032public class ReflectionUtil {
033
034        private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<String, Object>();
035
036        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);
037
038        public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) {
039                LinkedHashSet<Method> retVal = new LinkedHashSet<Method>();
040                for (Method next : theClazz.getDeclaredMethods()) {
041                        try {
042                                Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
043                                retVal.add(method);
044                        } catch (NoSuchMethodException e) {
045                                retVal.add(next);
046                        } catch (SecurityException e) {
047                                retVal.add(next);
048                        }
049                }
050                return retVal;
051        }
052
053        public static Class<?> getGenericCollectionTypeOfField(Field next) {
054                Class<?> type;
055                ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
056                Type firstArg = collectionType.getActualTypeArguments()[0];
057                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
058                        ParameterizedType pt = ((ParameterizedType) firstArg);
059                        type = (Class<?>) pt.getRawType();
060                } else {
061                        type = (Class<?>) firstArg;
062                }
063                return type;
064        }
065
066        /**
067         * For a field of type List<Enumeration<Foo>>, returns Foo
068         */
069        public static Class<?> getGenericCollectionTypeOfFieldWithSecondOrderForList(Field next) {
070                if (!List.class.isAssignableFrom(next.getType())) {
071                        return getGenericCollectionTypeOfField(next);
072                }
073
074                Class<?> type;
075                ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
076                Type firstArg = collectionType.getActualTypeArguments()[0];
077                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
078                        ParameterizedType pt = ((ParameterizedType) firstArg);
079                        Type pt2 = pt.getActualTypeArguments()[0];
080                        return (Class<?>) pt2;
081                }
082                type = (Class<?>) firstArg;
083                return type;
084        }
085
086        public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
087                Class<?> type;
088                Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
089                if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) {
090                        return null;
091                }
092                ParameterizedType collectionType = (ParameterizedType) genericParameterType;
093                Type firstArg = collectionType.getActualTypeArguments()[0];
094                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
095                        ParameterizedType pt = ((ParameterizedType) firstArg);
096                        type = (Class<?>) pt.getRawType();
097                } else {
098                        type = (Class<?>) firstArg;
099                }
100                return type;
101        }
102
103        @SuppressWarnings({ "rawtypes" })
104        public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
105                Class<?> type;
106                Type genericReturnType = theMethod.getGenericReturnType();
107                if (!(genericReturnType instanceof ParameterizedType)) {
108                        return null;
109                }
110                ParameterizedType collectionType = (ParameterizedType) genericReturnType;
111                Type firstArg = collectionType.getActualTypeArguments()[0];
112                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
113                        ParameterizedType pt = ((ParameterizedType) firstArg);
114                        type = (Class<?>) pt.getRawType();
115                } else if (firstArg instanceof TypeVariable<?>) {
116                        Type decl = ((TypeVariable) firstArg).getBounds()[0];
117                        return (Class<?>) decl;
118                } else if (firstArg instanceof WildcardType) {
119                        Type decl = ((WildcardType) firstArg).getUpperBounds()[0];
120                        return (Class<?>) decl;
121                } else {
122                        type = (Class<?>) firstArg;
123                }
124                return type;
125        }
126
127        public static boolean isInstantiable(Class<?> theType) {
128                return !theType.isInterface() && !Modifier.isAbstract(theType.getModifiers());
129        }
130
131        /**
132         * Instantiate a class by no-arg constructor, throw {@link ConfigurationException} if we fail to do so
133         */
134        @CoverageIgnore
135        public static <T> T newInstance(Class<T> theType) {
136                Validate.notNull(theType, "theType must not be null");
137                try {
138                        return theType.newInstance();
139                } catch (Exception e) {
140                        throw new ConfigurationException("Failed to instantiate " + theType.getName(), e);
141                }
142        }
143
144        public static <T> T newInstance(Class<T> theType, Class<?> theArgumentType, Object theArgument) {
145                Validate.notNull(theType, "theType must not be null");
146                try {
147                        Constructor<T> constructor = theType.getConstructor(theArgumentType);
148                        return constructor.newInstance(theArgument);
149                } catch (Exception e) {
150                        throw new ConfigurationException("Failed to instantiate " + theType.getName(), e);
151                }
152        }
153
154        public static Object newInstanceOfFhirServerType(String theType) {
155                String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
156                String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
157                Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
158                return fhirServerVersion;
159        }
160
161        @SuppressWarnings("unchecked")
162        public static <EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> ca.uhn.fhir.context.support.IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> newInstanceOfFhirProfileValidationSupport(
163                        String theType) {
164                String errorMessage = "Unable to instantiate validation support! Please make sure that hapi-fhir-validation and the appropriate structures JAR are on your classpath!";
165                String wantedType = "ca.uhn.fhir.context.support.IContextValidationSupport";
166                Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
167                return (IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>) fhirServerVersion;
168        }
169
170        private static Object newInstanceOfType(String theType, String errorMessage, String wantedType) {
171                Object fhirServerVersion = ourFhirServerVersions.get(theType);
172                if (fhirServerVersion == null) {
173                        try {
174                                Class<?> type = Class.forName(theType);
175                                Class<?> serverType = Class.forName(wantedType);
176                                Validate.isTrue(serverType.isAssignableFrom(type));
177                                fhirServerVersion = type.newInstance();
178                        } catch (Exception e) {
179                                throw new ConfigurationException(errorMessage, e);
180                        }
181
182                        ourFhirServerVersions.put(theType, fhirServerVersion);
183                }
184                return fhirServerVersion;
185        }
186
187        @SuppressWarnings("unchecked")
188        public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType) {
189                try {
190                        Class<?> clazz = Class.forName(theClassName);
191                        if (!theType.isAssignableFrom(clazz)) {
192                                throw new ConfigurationException(theClassName + " is not assignable to " + theType);
193                        }
194                        return (T) clazz.newInstance();
195                } catch (ConfigurationException e) {
196                        throw e;
197                } catch (Exception e) {
198                        ourLog.info("Failed to instantiate {}: {}", theClassName, e.toString());
199                        return null;
200                }
201        }
202
203}