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