001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.reflect; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.lang.reflect.*; 022import java.util.*; 023 024import org.apache.juneau.commons.inject.*; 025 026/** 027 * Utility class providing convenient static methods for creating reflection info objects. 028 * 029 * <p> 030 * This class provides static factory methods that convert standard Java reflection objects 031 * ({@link Class}, {@link Method}, {@link Field}, {@link Constructor}) to their corresponding 032 * info wrapper objects ({@link ClassInfo}, {@link MethodInfo}, {@link FieldInfo}, {@link ConstructorInfo}). 033 * 034 * <h5 class='section'>Features:</h5> 035 * <ul class='spaced-list'> 036 * <li>Convenient factory methods - convert reflection objects to info wrappers 037 * <li>Null-safe - methods handle <jk>null</jk> inputs gracefully 038 * <li>Unified API - consistent method naming across all reflection types 039 * </ul> 040 * 041 * <h5 class='section'>Use Cases:</h5> 042 * <ul class='spaced-list'> 043 * <li>Converting reflection objects to info wrappers in a consistent way 044 * <li>Simplifying code that works with both reflection and info objects 045 * <li>Providing a centralized location for reflection-to-info conversions 046 * </ul> 047 * 048 * <h5 class='section'>Usage:</h5> 049 * <p class='bjava'> 050 * <jc>// Convert Class to ClassInfo</jc> 051 * ClassInfo <jv>ci</jv> = ReflectionUtils.<jsm>info</jsm>(MyClass.<jk>class</jk>); 052 * 053 * <jc>// Convert Method to MethodInfo</jc> 054 * Method <jv>m</jv> = MyClass.<jk>class</jk>.getMethod(<js>"myMethod"</js>); 055 * MethodInfo <jv>mi</jv> = ReflectionUtils.<jsm>info</jsm>(<jv>m</jv>); 056 * 057 * <jc>// Convert Field to FieldInfo</jc> 058 * Field <jv>f</jv> = MyClass.<jk>class</jk>.getField(<js>"myField"</js>); 059 * FieldInfo <jv>fi</jv> = ReflectionUtils.<jsm>info</jsm>(<jv>f</jv>); 060 * 061 * <jc>// Convert Constructor to ConstructorInfo</jc> 062 * Constructor<?> <jv>c</jv> = MyClass.<jk>class</jk>.getConstructor(); 063 * ConstructorInfo <jv>ci2</jv> = ReflectionUtils.<jsm>info</jsm>(<jv>c</jv>); 064 * </p> 065 * 066 * <h5 class='section'>Null Handling:</h5> 067 * <p> 068 * All methods in this class handle <jk>null</jk> inputs gracefully, returning <jk>null</jk> if the 069 * input is <jk>null</jk>. This makes it safe to use in scenarios where reflection objects may be <jk>null</jk>. 070 * 071 * <h5 class='section'>See Also:</h5><ul> 072 * <li class='jc'>{@link ClassInfo} - Class introspection wrapper 073 * <li class='jc'>{@link MethodInfo} - Method introspection wrapper 074 * <li class='jc'>{@link FieldInfo} - Field introspection wrapper 075 * <li class='jc'>{@link ConstructorInfo} - Constructor introspection wrapper 076 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a> 077 * </ul> 078 */ 079public class ReflectionUtils { 080 081 /** 082 * Prevents instantiation. 083 */ 084 private ReflectionUtils() {} 085 086 /** 087 * Returns the {@link ClassInfoTyped} wrapper for the specified class. 088 * 089 * @param <T> The class type. 090 * @param o The class to wrap. Can be <jk>null</jk>. 091 * @return The {@link ClassInfoTyped} wrapper, or <jk>null</jk> if the input is <jk>null</jk>. 092 */ 093 public static final <T> ClassInfoTyped<T> info(Class<T> o) { 094 return ClassInfo.of(o); 095 } 096 097 /** 098 * Returns the {@link ConstructorInfo} wrapper for the specified constructor. 099 * 100 * @param o The constructor to wrap. Can be <jk>null</jk>. 101 * @return The {@link ConstructorInfo} wrapper, or <jk>null</jk> if the input is <jk>null</jk>. 102 */ 103 public static final ConstructorInfo info(Constructor<?> o) { 104 return ConstructorInfo.of(o); 105 } 106 107 /** 108 * Returns the {@link FieldInfo} wrapper for the specified field. 109 * 110 * @param o The field to wrap. Can be <jk>null</jk>. 111 * @return The {@link FieldInfo} wrapper, or <jk>null</jk> if the input is <jk>null</jk>. 112 */ 113 public static final FieldInfo info(Field o) { 114 return FieldInfo.of(o); 115 } 116 117 /** 118 * Returns the {@link MethodInfo} wrapper for the specified method. 119 * 120 * @param o The method to wrap. Can be <jk>null</jk>. 121 * @return The {@link MethodInfo} wrapper, or <jk>null</jk> if the input is <jk>null</jk>. 122 */ 123 public static final MethodInfo info(Method o) { 124 return MethodInfo.of(o); 125 } 126 127 /** 128 * Returns the {@link ClassInfo} wrapper for the class of the specified object. 129 * 130 * @param o The object whose class to wrap. Can be <jk>null</jk>. 131 * @return The {@link ClassInfo} wrapper for the object's class, or <jk>null</jk> if the input is <jk>null</jk>. 132 */ 133 public static final ClassInfo info(Object o) { 134 return ClassInfo.of(o); 135 } 136 137 /** 138 * Converts a class to an enum class, suppressing rawtypes warnings. 139 * 140 * <p> 141 * This utility method wraps {@link Class#asSubclass(Class)} for enum classes and suppresses 142 * the rawtypes warning that occurs when converting to {@code Class<? extends Enum>} for use 143 * with methods like {@link EnumSet#allOf(Class)}. 144 * 145 * <h5 class='section'>Example:</h5> 146 * <p class='bjava'> 147 * <jc>// Instead of:</jc> 148 * <ja>@SuppressWarnings</ja>(<js>"rawtypes"</js>) 149 * Class<? <jk>extends</jk> Enum> <jv>enumClass</jv> = <jv>clazz</jv>.asSubclass(Enum.<jk>class</jk>); 150 * 151 * <jc>// Use:</jc> 152 * Class<? <jk>extends</jk> Enum> <jv>enumClass</jv> = ReflectionUtils.<jsm>asEnumClass</jsm>(<jv>clazz</jv>); 153 * </p> 154 * 155 * @param clazz The class to convert. Must be an enum class. Must not be <jk>null</jk>. 156 * @return The enum class as a raw type suitable for use with {@link EnumSet#allOf(Class)}. 157 * @throws ClassCastException if the class is not an enum class. 158 */ 159 @SuppressWarnings({ 160 "rawtypes" // Raw types necessary for enum class return 161 }) 162 public static Class<? extends Enum> asEnumClass(Class<?> clazz) { 163 return clazz.asSubclass(Enum.class); 164 } 165 166 /** 167 * Resolves and creates a collection, array, or map containing all beans of the element type from the bean store. 168 * 169 * <p> 170 * This is a helper method used internally by {@link ParameterInfo} and {@link FieldInfo} for resolving collection values. 171 * 172 * @param elementType The element type (for arrays/collections) or value type (for maps). 173 * @param beanStore The bean store to retrieve beans from. 174 * @param targetType The target collection type (must be a collection type). 175 * @return The collection, array, or map containing all beans of the element type, or <jk>null</jk> if not a supported collection type. 176 */ 177 static Object resolveCollectionValue(Class<?> elementType, BeanStore beanStore, ClassInfo targetType) { 178 if (elementType == null || ! targetType.isInjectCollectionType()) 179 return null; 180 181 // Get all beans of the element type 182 var beans = beanStore.getBeansOfType(elementType); 183 184 // Handle Map<String,T> - getBeansOfType already returns Map<String,T> 185 var inner = o(targetType.inner()).orElse(Object.class); 186 if (Map.class.isAssignableFrom(inner)) { 187 // Verify it's Map<String,T> by checking the parameterized type 188 Type parameterizedType = targetType.innerType(); 189 if (parameterizedType instanceof ParameterizedType parameterizedType2) { 190 var typeArgs = parameterizedType2.getActualTypeArguments(); 191 if (typeArgs.length >= 2 && typeArgs[0] == String.class) { 192 return new LinkedHashMap<>(beans); 193 } 194 } 195 return null; 196 } 197 198 var values = new ArrayList<>(beans.values()); 199 200 // Convert to appropriate collection/array type 201 if (targetType.isArray()) { 202 var array = Array.newInstance(elementType, values.size()); 203 for (var i = 0; i < values.size(); i++) 204 Array.set(array, i, values.get(i)); 205 return array; 206 } 207 208 if (eq(inner, Set.class)) { 209 return new LinkedHashSet<>(values); 210 } else if (eq(inner, List.class)) { 211 return values; 212 } 213 214 // Defensive check: return null if inner is null or not a supported collection type. 215 return null; 216 } 217}