001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.internal;
014
015import java.lang.reflect.*;
016import java.util.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.reflect.*;
020import org.apache.juneau.utils.*;
021
022/**
023 * Class-related utility methods.
024 */
025public final class ClassUtils {
026
027   /**
028    * Shortcut for calling {@link ClassInfo#of(Type)}.
029    *
030    * @param t The class being wrapped.
031    * @return The wrapped class.
032    */
033   public static ClassInfo getClassInfo(Type t) {
034      return ClassInfo.of(t);
035   }
036
037   /**
038    * Shortcut for calling {@link ClassInfo#of(Object)}.
039    *
040    * @param o The object whose class being wrapped.
041    * @return The wrapped class.
042    */
043   public static ClassInfo getClassInfo(Object o) {
044      return ClassInfo.of(o);
045   }
046
047   /**
048    * Shortcut for calling {@link MethodInfo#of(Method)}.
049    *
050    * @param m The method being wrapped.
051    * @return The wrapped method.
052    */
053   public static MethodInfo getMethodInfo(Method m) {
054      return MethodInfo.of(m);
055   }
056
057   /**
058    * Shortcut for calling {@link MethodInfo#of(ClassInfo, Method)}.
059    *
060    * @param c
061    *    The class containing the method.
062    *    <br>Note that this isn't necessarily the declaring class, but could be a subclass
063    *    of the declaring class.
064    * @param m The method being wrapped.
065    * @return The wrapped method.
066    */
067   public static MethodInfo getMethodInfo(Class<?> c, Method m) {
068      return MethodInfo.of(ClassInfo.of(c), m);
069   }
070
071   /**
072    * Shortcut for calling {@link FieldInfo#of(Field)}.
073    *
074    * @param f The field being wrapped.
075    * @return The wrapped field.
076    */
077   public static FieldInfo getFieldInfo(Field f) {
078      return FieldInfo.of(f);
079   }
080
081   /**
082    * Shortcut for calling {@link ConstructorInfo#of(Constructor)}.
083    *
084    * @param c The constructor being wrapped.
085    * @return The wrapped constructor.
086    */
087   public static ConstructorInfo getConstructorInfo(Constructor<?> c) {
088      return ConstructorInfo.of(c);
089   }
090
091   /**
092    * Given the specified list of objects, return readable names for the class types of the objects.
093    *
094    * @param o The objects.
095    * @return An array of readable class type strings.
096    */
097   public static ObjectList getFullClassNames(Object[] o) {
098      ObjectList l = new ObjectList();
099      for (int i = 0; i < o.length; i++)
100         l.add(o[i] == null ? "null" : ClassInfo.of((o[i].getClass())).getFullName());
101      return l;
102   }
103
104   /**
105    * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types.
106    *
107    * @param paramTypes The parameters types specified on a method.
108    * @param argTypes The class types of the arguments being passed to the method.
109    * @return <jk>true</jk> if the arguments match the parameters.
110    */
111   public static boolean argsMatch(Class<?>[] paramTypes, Class<?>[] argTypes) {
112      if (paramTypes.length == argTypes.length) {
113         for (int i = 0; i < paramTypes.length; i++)
114            if (! getClassInfo(paramTypes[i]).isParentOf(argTypes[i]))
115               return false;
116         return true;
117      }
118      return false;
119   }
120
121   /**
122    * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types.
123    *
124    * @param paramTypes The parameters types specified on a method.
125    * @param argTypes The class types of the arguments being passed to the method.
126    * @return <jk>true</jk> if the arguments match the parameters.
127    */
128   public static boolean argsMatch(List<ClassInfo> paramTypes, Class<?>[] argTypes) {
129      if (paramTypes.size() == argTypes.length) {
130         for (int i = 0; i < paramTypes.size(); i++)
131            if (! paramTypes.get(i).isParentOf(argTypes[i]))
132               return false;
133         return true;
134      }
135      return false;
136   }
137
138   /**
139    * Returns a number representing the number of arguments that match the specified parameters.
140    *
141    * @param paramTypes The parameters types specified on a method.
142    * @param argTypes The class types of the arguments being passed to the method.
143    * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
144    */
145   public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>... argTypes) {
146      int matches = 0;
147      outer: for (Class<?> p : paramTypes) {
148         ClassInfo pi = getClassInfo(p).getWrapperInfoIfPrimitive();
149         for (Class<?> a : argTypes) {
150            ClassInfo ai = getClassInfo(a).getWrapperInfoIfPrimitive();
151            if (pi.isParentOf(ai.inner())) {
152               matches++;
153               continue outer;
154            }
155         }
156         return -1;
157      }
158      return matches;
159   }
160
161   /**
162    * Returns a number representing the number of arguments that match the specified parameters.
163    *
164    * @param paramTypes The parameters types specified on a method.
165    * @param argTypes The class types of the arguments being passed to the method.
166    * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
167    */
168   public static int fuzzyArgsMatch(Class<?>[] paramTypes, ClassInfo... argTypes) {
169      int matches = 0;
170      outer: for (Class<?> p : paramTypes) {
171         ClassInfo pi = getClassInfo(p).getWrapperInfoIfPrimitive();
172         for (ClassInfo a : argTypes) {
173            ClassInfo ai = a.getWrapperInfoIfPrimitive();
174            if (pi.isParentOf(ai.inner())) {
175               matches++;
176               continue outer;
177            }
178         }
179         return -1;
180      }
181      return matches;
182   }
183
184   /**
185    * Returns a number representing the number of arguments that match the specified parameters.
186    *
187    * @param paramTypes The parameters types specified on a method.
188    * @param argTypes The class types of the arguments being passed to the method.
189    * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
190    */
191   public static int fuzzyArgsMatch(List<ClassInfo> paramTypes, Class<?>... argTypes) {
192      int matches = 0;
193      outer: for (ClassInfo p : paramTypes) {
194         p = p.getWrapperInfoIfPrimitive();
195         for (Class<?> a : argTypes) {
196            if (p.isParentOf(a)) {
197               matches++;
198               continue outer;
199            }
200         }
201         return -1;
202      }
203      return matches;
204   }
205
206   /**
207    * Returns the class types for the specified arguments.
208    *
209    * @param args The objects we're getting the classes of.
210    * @return The classes of the arguments.
211    */
212   public static Class<?>[] getClasses(Object...args) {
213      Class<?>[] pt = new Class<?>[args.length];
214      for (int i = 0; i < args.length; i++)
215         pt[i] = args[i] == null ? null : args[i].getClass();
216      return pt;
217   }
218
219   /**
220    * Creates an instance of the specified class.
221    *
222    * @param c
223    *    The class to cast to.
224    * @param c2
225    *    The class to instantiate.
226    *    Can also be an instance of the class.
227    * @return
228    *    The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface.
229    * @throws
230    *    RuntimeException if constructor could not be found or called.
231    */
232   public static <T> T castOrCreate(Class<T> c, Object c2) {
233      return castOrCreateFromOuter(null, c, c2, false);
234   }
235
236   /**
237    * Creates an instance of the specified class.
238    *
239    * @param c
240    *    The class to cast to.
241    * @param c2
242    *    The class to instantiate.
243    *    Can also be an instance of the class.
244    * @param fuzzyArgs
245    *    Use fuzzy constructor arg matching.
246    *    <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
247    *    <br>No-arg constructors are also used if no other constructors are found.
248    * @param args
249    *    The arguments to pass to the constructor.
250    * @return
251    *    The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface.
252    * @throws
253    *    RuntimeException if constructor could not be found or called.
254    */
255   public static <T> T castOrCreate(Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
256      return castOrCreateFromOuter(null, c, c2, fuzzyArgs, args);
257   }
258
259   /**
260    * Creates an instance of the specified class from within the context of another object.
261    *
262    * @param outer
263    *    The outer object.
264    *    Can be <jk>null</jk>.
265    * @param c
266    *    The class to cast to.
267    * @param c2
268    *    The class to instantiate.
269    *    Can also be an instance of the class.
270    * @param fuzzyArgs
271    *    Use fuzzy constructor arg matching.
272    *    <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
273    *    <br>No-arg constructors are also used if no other constructors are found.
274    * @param args
275    *    The arguments to pass to the constructor.
276    * @return
277    *    The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface.
278    * @throws
279    *    RuntimeException if constructor could not be found or called.
280    */
281   @SuppressWarnings("unchecked")
282   public static <T> T castOrCreateFromOuter(Object outer, Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
283      if (c2 == null)
284         return null;
285      if (c2 instanceof Class) {
286         try {
287            ClassInfo c3 = getClassInfo((Class<?>)c2);
288            if (c3.isInterface() || c3.isAbstract())
289               return null;
290
291            // First look for an exact match.
292            ConstructorInfo con = c3.getPublicConstructor(args);
293            if (con != null)
294               return con.<T>invoke(args);
295
296            // Next look for an exact match including the outer.
297            if (outer != null) {
298               args = new AList<>().append(outer).appendAll(args).toArray();
299               con = c3.getPublicConstructor(args);
300               if (con != null)
301                  return con.<T>invoke(args);
302            }
303
304            // Finally use fuzzy matching.
305            if (fuzzyArgs) {
306               con = c3.getPublicConstructorFuzzy(args);
307               if (con != null)
308                  return con.<T>invoke(getMatchingArgs(con.getParamTypes(), args));
309            }
310
311            throw new FormattedRuntimeException("Could not instantiate class {0}/{1}.  Constructor not found.", c.getName(), c2);
312         } catch (Exception e) {
313            throw new FormattedRuntimeException(e, "Could not instantiate class {0}", c.getName());
314         }
315      } else if (getClassInfo(c).isParentOf(c2.getClass())) {
316         return (T)c2;
317      } else {
318         throw new FormattedRuntimeException("Object of type {0} found but was expecting {1}.", c2.getClass(), c.getClass());
319      }
320   }
321
322   /**
323    * Matches arguments to a list of parameter types.
324    *
325    * <p>
326    * Extra parameters are ignored.
327    * <br>Missing parameters are left null.
328    *
329    * @param paramTypes The parameter types.
330    * @param args The arguments to match to the parameter types.
331    * @return
332    *    An array of parameters.
333    */
334   public static Object[] getMatchingArgs(Class<?>[] paramTypes, Object... args) {
335      Object[] params = new Object[paramTypes.length];
336      for (int i = 0; i < paramTypes.length; i++) {
337         ClassInfo pt = getClassInfo(paramTypes[i]).getWrapperInfoIfPrimitive();
338         for (int j = 0; j < args.length; j++) {
339            if (args[j] != null && pt.isParentOf(args[j].getClass())) {
340               params[i] = args[j];
341               break;
342            }
343         }
344      }
345      return params;
346   }
347
348   /**
349    * Matches arguments to a list of parameter types.
350    *
351    * <p>
352    * Extra parameters are ignored.
353    * <br>Missing parameters are left null.
354    *
355    * @param paramTypes The parameter types.
356    * @param args The arguments to match to the parameter types.
357    * @return
358    *    An array of parameters.
359    */
360   public static Object[] getMatchingArgs(List<ClassInfo> paramTypes, Object... args) {
361      Object[] params = new Object[paramTypes.size()];
362      for (int i = 0; i < paramTypes.size(); i++) {
363         ClassInfo pt = paramTypes.get(i).getWrapperInfoIfPrimitive();
364         for (int j = 0; j < args.length; j++) {
365            if (pt.isParentOf(args[j].getClass())) {
366               params[i] = args[j];
367               break;
368            }
369         }
370      }
371      return params;
372   }
373
374   /**
375    * Constructs a new instance of the specified class from the specified string.
376    *
377    * <p>
378    * Class must be one of the following:
379    * <ul>
380    *    <li>Have a public constructor that takes in a single <c>String</c> argument.
381    *    <li>Have a static <c>fromString(String)</c> (or related) method.
382    *    <li>Be an <c>enum</c>.
383    * </ul>
384    *
385    * @param c The class.
386    * @param s The string to create the instance from.
387    * @return A new object instance, or <jk>null</jk> if a method for converting the string to an object could not be found.
388    */
389   public static <T> T fromString(Class<T> c, String s) {
390      Mutater<String,T> t = Mutaters.get(String.class, c);
391      return t == null ? null : t.mutate(s);
392   }
393
394   /**
395    * Converts an object to a string.
396    *
397    * <p>
398    * Normally, this is just going to call <c>toString()</c> on the object.
399    * However, the {@link Locale} and {@link TimeZone} objects are treated special so that the returned value
400    * works with the {@link #fromString(Class, String)} method.
401    *
402    * @param o The object to convert to a string.
403    * @return The stringified object, or <jk>null</jk> if the object was <jk>null</jk>.
404    */
405   @SuppressWarnings({ "unchecked" })
406   public static String toString(Object o) {
407      if (o == null)
408         return null;
409      Mutater<Object,String> t = (Mutater<Object,String>)Mutaters.get(o.getClass(), String.class);
410      return t == null ? o.toString() : t.mutate(o);
411   }
412
413   /**
414    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
415    *
416    * @param x The constructor.
417    * @return <jk>true</jk> if call was successful.
418    */
419   public static boolean setAccessible(Constructor<?> x) {
420      try {
421         if (! (x == null || x.isAccessible()))
422            x.setAccessible(true);
423         return true;
424      } catch (SecurityException e) {
425         return false;
426      }
427   }
428
429   /**
430    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
431    *
432    * @param x The method.
433    * @return <jk>true</jk> if call was successful.
434    */
435   public static boolean setAccessible(Method x) {
436      try {
437         if (! (x == null || x.isAccessible()))
438            x.setAccessible(true);
439         return true;
440      } catch (SecurityException e) {
441         return false;
442      }
443   }
444
445   /**
446    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
447    *
448    * @param x The field.
449    * @return <jk>true</jk> if call was successful.
450    */
451   public static boolean setAccessible(Field x) {
452      try {
453         if (! (x == null || x.isAccessible()))
454            x.setAccessible(true);
455         return true;
456      } catch (SecurityException e) {
457         return false;
458      }
459   }
460
461   /**
462    * Returns the specified type as a <c>Class</c>.
463    *
464    * <p>
465    * If it's already a <c>Class</c>, it just does a cast.
466    * <br>If it's a <c>ParameterizedType</c>, it returns the raw type.
467    *
468    * @param t The type to convert.
469    * @return The type converted to a <c>Class</c>, or <jk>null</jk> if it could not be converted.
470    */
471   public static Class<?> toClass(Type t) {
472      if (t instanceof Class)
473         return (Class<?>)t;
474      if (t instanceof ParameterizedType) {
475         ParameterizedType pt = (ParameterizedType)t;
476         // The raw type should always be a class (right?)
477         return (Class<?>)pt.getRawType();
478      }
479      return null;
480   }
481}