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}