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