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 */
022
023import java.lang.reflect.Field;
024import java.lang.reflect.Method;
025import java.lang.reflect.ParameterizedType;
026import java.lang.reflect.Type;
027import java.lang.reflect.TypeVariable;
028import java.lang.reflect.WildcardType;
029import java.util.LinkedHashSet;
030import java.util.List;
031
032import org.apache.commons.lang3.Validate;
033
034import ca.uhn.fhir.context.ConfigurationException;
035import javassist.Modifier;
036
037public class ReflectionUtil {
038
039        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);
040
041        public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) {
042                LinkedHashSet<Method> retVal = new LinkedHashSet<Method>();
043                for (Method next : theClazz.getDeclaredMethods()) {
044                        try {
045                                Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
046                                retVal.add(method);
047                        } catch (NoSuchMethodException e) {
048                                retVal.add(next);
049                        } catch (SecurityException e) {
050                                retVal.add(next);
051                        }
052                }
053                return retVal;
054        }
055
056        public static Class<?> getGenericCollectionTypeOfField(Field next) {
057                Class<?> type;
058                ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
059                Type firstArg = collectionType.getActualTypeArguments()[0];
060                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
061                        ParameterizedType pt = ((ParameterizedType) firstArg);
062                        type = (Class<?>) pt.getRawType();
063                } else {
064                        type = (Class<?>) firstArg;
065                }
066                return type;
067        }
068
069        /**
070         * For a field of type List<Enumeration<Foo>>, returns Foo
071         */
072        public static Class<?> getGenericCollectionTypeOfFieldWithSecondOrderForList(Field next) {
073                if (!List.class.isAssignableFrom(next.getType())) {
074                        return getGenericCollectionTypeOfField(next);
075                }
076                
077                Class<?> type;
078                ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
079                Type firstArg = collectionType.getActualTypeArguments()[0];
080                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
081                        ParameterizedType pt = ((ParameterizedType) firstArg);
082                        Type pt2 = pt.getActualTypeArguments()[0];
083                        return (Class<?>) pt2;
084                }
085                type = (Class<?>) firstArg;
086                return type;
087        }
088
089        public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
090                Class<?> type;
091                Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
092                if (Class.class.equals(genericParameterType)) {
093                        return null;
094                }
095                ParameterizedType collectionType = (ParameterizedType) genericParameterType;
096                Type firstArg = collectionType.getActualTypeArguments()[0];
097                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
098                        ParameterizedType pt = ((ParameterizedType) firstArg);
099                        type = (Class<?>) pt.getRawType();
100                } else {
101                        type = (Class<?>) firstArg;
102                }
103                return type;
104        }
105
106        @SuppressWarnings({ "rawtypes" })
107        public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
108                Class<?> type;
109                Type genericReturnType = theMethod.getGenericReturnType();
110                if (!(genericReturnType instanceof ParameterizedType)) {
111                        return null;
112                }
113                ParameterizedType collectionType = (ParameterizedType) genericReturnType;
114                Type firstArg = collectionType.getActualTypeArguments()[0];
115                if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
116                        ParameterizedType pt = ((ParameterizedType) firstArg);
117                        type = (Class<?>) pt.getRawType();
118                } else if (firstArg instanceof TypeVariable<?>) {
119                        Type decl = ((TypeVariable) firstArg).getBounds()[0];
120                        return (Class<?>) decl;
121                } else if (firstArg instanceof WildcardType) {
122                        Type decl = ((WildcardType) firstArg).getUpperBounds()[0];
123                        return (Class<?>) decl;
124                } else {
125                        type = (Class<?>) firstArg;
126                }
127                return type;
128        }
129
130        /**
131         * Instantiate a class by no-arg constructor, throw {@link ConfigurationException} if we fail to do so
132         */
133        @CoverageIgnore
134        public static <T> T newInstance(Class<T> theType) {
135                Validate.notNull(theType, "theType must not be null");
136                try {
137                        return theType.newInstance();
138                } catch (Exception e) {
139                        throw new ConfigurationException("Failed to instantiate " + theType.getName(), e);
140                }
141        }
142
143        @SuppressWarnings("unchecked")
144        public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType) {
145                try {
146                        Class<?> clazz = Class.forName(theClassName);
147                        if (!theType.isAssignableFrom(clazz)) {
148                                throw new ConfigurationException(theClassName + " is not assignable to " + theType);
149                        }
150                        return (T) clazz.newInstance();
151                } catch (ConfigurationException e) {
152                        throw e;
153                } catch (Exception e) {
154                        ourLog.info("Failed to instantiate {}: {}", theClassName, e.toString());
155                        return null;
156                }
157        }
158
159        public static boolean isInstantiable(Class<?> theType) {
160                return !theType.isInterface() && !Modifier.isAbstract(theType.getModifiers());
161        }
162
163}