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}