All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.uhn.fhir.util.ReflectionUtil Maven / Gradle / Ivy

There is a newer version: 7.4.5
Show newest version
/*
 * #%L
 * HAPI FHIR - Core Library
 * %%
 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package ca.uhn.fhir.util;

import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import jakarta.annotation.Nonnull;
import org.apache.commons.lang3.Validate;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class ReflectionUtil {

	public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
	public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
	private static final ConcurrentHashMap ourFhirServerVersions = new ConcurrentHashMap<>();
	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);

	/**
	 * Non instantiable
	 */
	private ReflectionUtil() {
		super();
	}

	/**
	 * Returns all methods declared against {@literal theClazz}. This method returns a predictable order, which is
	 * sorted by method name and then by parameters.
	 * 

* This method does not include superclass methods (see {@link #getDeclaredMethods(Class, boolean)} if you * want to include those. *

*/ public static List getDeclaredMethods(Class theClazz) { return getDeclaredMethods(theClazz, false); } /** * Returns all methods declared against {@literal theClazz}. This method returns a predictable order, which is * sorted by method name and then by parameters. */ public static List getDeclaredMethods(Class theClazz, boolean theIncludeMethodsFromSuperclasses) { HashMap foundMethods = new HashMap<>(); populateDeclaredMethodsMap(theClazz, foundMethods, theIncludeMethodsFromSuperclasses); List retVal = new ArrayList<>(foundMethods.values()); retVal.sort((Comparator.comparing(ReflectionUtil::describeMethodInSortFriendlyWay))); return retVal; } private static void populateDeclaredMethodsMap( Class theClazz, HashMap foundMethods, boolean theIncludeMethodsFromSuperclasses) { Method[] declaredMethods = theClazz.getDeclaredMethods(); for (Method next : declaredMethods) { if (Modifier.isAbstract(next.getModifiers()) || Modifier.isStatic(next.getModifiers()) || Modifier.isPrivate(next.getModifiers())) { continue; } String description = next.getName() + Arrays.asList(next.getParameterTypes()); if (!foundMethods.containsKey(description)) { try { Method method = theClazz.getMethod(next.getName(), next.getParameterTypes()); foundMethods.put(description, method); } catch (NoSuchMethodException | SecurityException e) { foundMethods.put(description, next); } } } if (theIncludeMethodsFromSuperclasses && !theClazz.getSuperclass().equals(Object.class)) { populateDeclaredMethodsMap(theClazz.getSuperclass(), foundMethods, theIncludeMethodsFromSuperclasses); } } /** * Returns a description like startsWith params(java.lang.String, int) returns(boolean). * The format is chosen in order to provide a predictable and useful sorting order. */ public static String describeMethodInSortFriendlyWay(Method theMethod) { StringBuilder b = new StringBuilder(); b.append(theMethod.getName()); b.append(" returns("); b.append(theMethod.getReturnType().getName()); b.append(") params("); Class[] parameterTypes = theMethod.getParameterTypes(); for (int i = 0, parameterTypesLength = parameterTypes.length; i < parameterTypesLength; i++) { if (i > 0) { b.append(", "); } Class next = parameterTypes[i]; b.append(next.getName()); } b.append(")"); return b.toString(); } public static Class getGenericCollectionTypeOfField(Field next) { ParameterizedType collectionType = (ParameterizedType) next.getGenericType(); return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]); } /** * For a field of type List>, returns Foo */ public static Class getGenericCollectionTypeOfFieldWithSecondOrderForList(Field next) { if (!List.class.isAssignableFrom(next.getType())) { return getGenericCollectionTypeOfField(next); } Class type; ParameterizedType collectionType = (ParameterizedType) next.getGenericType(); Type firstArg = collectionType.getActualTypeArguments()[0]; if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) { ParameterizedType pt = ((ParameterizedType) firstArg); Type pt2 = pt.getActualTypeArguments()[0]; return (Class) pt2; } type = (Class) firstArg; return type; } public static Class getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) { Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex]; if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) { return null; } ParameterizedType collectionType = (ParameterizedType) genericParameterType; return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]); } public static Class getGenericCollectionTypeOfMethodReturnType(Method theMethod) { Type genericReturnType = theMethod.getGenericReturnType(); if (!(genericReturnType instanceof ParameterizedType)) { return null; } ParameterizedType collectionType = (ParameterizedType) genericReturnType; return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]); } @SuppressWarnings({"rawtypes"}) private static Class getGenericCollectionTypeOf(Type theType) { Class type; if (ParameterizedType.class.isAssignableFrom(theType.getClass())) { ParameterizedType pt = ((ParameterizedType) theType); type = (Class) pt.getRawType(); } else if (theType instanceof TypeVariable) { Type decl = ((TypeVariable) theType).getBounds()[0]; return (Class) decl; } else if (theType instanceof WildcardType) { Type decl = ((WildcardType) theType).getUpperBounds()[0]; return (Class) decl; } else { type = (Class) theType; } return type; } public static boolean isInstantiable(Class theType) { return !theType.isInterface() && !Modifier.isAbstract(theType.getModifiers()); } /** * Instantiate a class by no-arg constructor, throw {@link ConfigurationException} if we fail to do so */ public static T newInstance(Class theType) { Validate.notNull(theType, "theType must not be null"); try { return theType.getConstructor().newInstance(); } catch (Exception e) { throw new ConfigurationException(Msg.code(1784) + "Failed to instantiate " + theType.getName(), e); } } public static T newInstance(Class theType, Class theArgumentType, Object theArgument) { Validate.notNull(theType, "theType must not be null"); try { Constructor constructor = theType.getConstructor(theArgumentType); return constructor.newInstance(theArgument); } catch (Exception e) { throw new ConfigurationException(Msg.code(1785) + "Failed to instantiate " + theType.getName(), e); } } public static Object newInstanceOfFhirServerType(String theType) { String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!"; String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer"; return newInstanceOfType(theType, theType, errorMessage, wantedType, new Class[0], new Object[0]); } private static Object newInstanceOfType( String theKey, String theType, String errorMessage, String wantedType, Class[] theParameterArgTypes, Object[] theConstructorArgs) { Object fhirServerVersion = ourFhirServerVersions.get(theKey); if (fhirServerVersion == null) { try { Class type = Class.forName(theType); Class serverType = Class.forName(wantedType); Validate.isTrue(serverType.isAssignableFrom(type)); fhirServerVersion = type.getConstructor(theParameterArgTypes).newInstance(theConstructorArgs); } catch (Exception e) { throw new ConfigurationException(Msg.code(1786) + errorMessage, e); } ourFhirServerVersions.put(theKey, fhirServerVersion); } return fhirServerVersion; } public static T newInstanceOrReturnNull(String theClassName, Class theType) { return newInstanceOrReturnNull(theClassName, theType, EMPTY_CLASS_ARRAY, EMPTY_OBJECT_ARRAY); } @SuppressWarnings("unchecked") public static T newInstanceOrReturnNull( String theClassName, Class theType, Class[] theArgTypes, Object[] theArgs) { try { return newInstance(theClassName, theType, theArgTypes, theArgs); } catch (ConfigurationException e) { throw e; } catch (Exception e) { ourLog.info("Failed to instantiate {}: {}", theClassName, e.toString()); return null; } } @Nonnull public static T newInstance(String theClassName, Class theType, Class[] theArgTypes, Object[] theArgs) { try { Class clazz = Class.forName(theClassName); if (!theType.isAssignableFrom(clazz)) { throw new ConfigurationException(Msg.code(1787) + theClassName + " is not assignable to " + theType); } return (T) clazz.getConstructor(theArgTypes).newInstance(theArgs); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new InternalErrorException(Msg.code(2330) + e.getMessage(), e); } } public static boolean typeExists(String theName) { try { Class.forName(theName); return true; } catch (ClassNotFoundException theE) { return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy