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&lt;?&gt; <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&lt;? <jk>extends</jk> Enum&gt; <jv>enumClass</jv> = <jv>clazz</jv>.asSubclass(Enum.<jk>class</jk>);
150    *
151    *    <jc>// Use:</jc>
152    *    Class&lt;? <jk>extends</jk> Enum&gt; <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}