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    * Given the specified list of objects, return readable names for the class types of the objects.
029    *
030    * @param o The objects.
031    * @return An array of readable class type strings.
032    */
033   public static ObjectList getFullClassNames(Object[] o) {
034      ObjectList l = new ObjectList();
035      for (int i = 0; i < o.length; i++)
036         l.add(o[i] == null ? "null" : ClassInfo.of((o[i].getClass())).getFullName());
037      return l;
038   }
039
040   /**
041    * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types.
042    *
043    * @param paramTypes The parameters types specified on a method.
044    * @param argTypes The class types of the arguments being passed to the method.
045    * @return <jk>true</jk> if the arguments match the parameters.
046    */
047   public static boolean argsMatch(Class<?>[] paramTypes, Class<?>[] argTypes) {
048      if (paramTypes.length == argTypes.length) {
049         for (int i = 0; i < paramTypes.length; i++)
050            if (! ClassInfo.of(paramTypes[i]).isParentOf(argTypes[i]))
051               return false;
052         return true;
053      }
054      return false;
055   }
056
057   /**
058    * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types.
059    *
060    * @param paramTypes The parameters types specified on a method.
061    * @param argTypes The class types of the arguments being passed to the method.
062    * @return <jk>true</jk> if the arguments match the parameters.
063    */
064   public static boolean argsMatch(List<ClassInfo> paramTypes, Class<?>[] argTypes) {
065      if (paramTypes.size() == argTypes.length) {
066         for (int i = 0; i < paramTypes.size(); i++)
067            if (! paramTypes.get(i).isParentOf(argTypes[i]))
068               return false;
069         return true;
070      }
071      return false;
072   }
073
074   /**
075    * Returns a number representing the number of arguments that match the specified parameters.
076    *
077    * @param paramTypes The parameters types specified on a method.
078    * @param argTypes The class types of the arguments being passed to the method.
079    * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
080    */
081   public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>... argTypes) {
082      int matches = 0;
083      outer: for (Class<?> p : paramTypes) {
084         ClassInfo pi = ClassInfo.of(p).getWrapperInfoIfPrimitive();
085         for (Class<?> a : argTypes) {
086            ClassInfo ai = ClassInfo.of(a).getWrapperInfoIfPrimitive();
087            if (pi.isParentOf(ai.inner())) {
088               matches++;
089               continue outer;
090            }
091         }
092         return -1;
093      }
094      return matches;
095   }
096
097   /**
098    * Returns a number representing the number of arguments that match the specified parameters.
099    *
100    * @param paramTypes The parameters types specified on a method.
101    * @param argTypes The class types of the arguments being passed to the method.
102    * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
103    */
104   public static int fuzzyArgsMatch(Class<?>[] paramTypes, ClassInfo... argTypes) {
105      int matches = 0;
106      outer: for (Class<?> p : paramTypes) {
107         ClassInfo pi = ClassInfo.of(p).getWrapperInfoIfPrimitive();
108         for (ClassInfo a : argTypes) {
109            ClassInfo ai = a.getWrapperInfoIfPrimitive();
110            if (pi.isParentOf(ai.inner())) {
111               matches++;
112               continue outer;
113            }
114         }
115         return -1;
116      }
117      return matches;
118   }
119
120   /**
121    * Returns a number representing the number of arguments that match the specified parameters.
122    *
123    * @param paramTypes The parameters types specified on a method.
124    * @param argTypes The class types of the arguments being passed to the method.
125    * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
126    */
127   public static int fuzzyArgsMatch(List<ClassInfo> paramTypes, Class<?>... argTypes) {
128      int matches = 0;
129      outer: for (ClassInfo p : paramTypes) {
130         p = p.getWrapperInfoIfPrimitive();
131         for (Class<?> a : argTypes) {
132            if (p.isParentOf(a)) {
133               matches++;
134               continue outer;
135            }
136         }
137         return -1;
138      }
139      return matches;
140   }
141
142   /**
143    * Returns the class types for the specified arguments.
144    *
145    * @param args The objects we're getting the classes of.
146    * @return The classes of the arguments.
147    */
148   public static Class<?>[] getClasses(Object...args) {
149      Class<?>[] pt = new Class<?>[args.length];
150      for (int i = 0; i < args.length; i++)
151         pt[i] = args[i] == null ? null : args[i].getClass();
152      return pt;
153   }
154
155   /**
156    * Creates an instance of the specified class.
157    *
158    * @param c
159    *    The class to cast to.
160    * @param c2
161    *    The class to instantiate.
162    *    Can also be an instance of the class.
163    * @return
164    *    The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface.
165    * @throws
166    *    RuntimeException if constructor could not be found or called.
167    */
168   public static <T> T castOrCreate(Class<T> c, Object c2) {
169      return castOrCreateFromOuter(null, c, c2, false);
170   }
171
172   /**
173    * Creates an instance of the specified class.
174    *
175    * @param c
176    *    The class to cast to.
177    * @param c2
178    *    The class to instantiate.
179    *    Can also be an instance of the class.
180    * @param fuzzyArgs
181    *    Use fuzzy constructor arg matching.
182    *    <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
183    *    <br>No-arg constructors are also used if no other constructors are found.
184    * @param args
185    *    The arguments to pass to the constructor.
186    * @return
187    *    The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface.
188    * @throws
189    *    RuntimeException if constructor could not be found or called.
190    */
191   public static <T> T castOrCreate(Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
192      return castOrCreateFromOuter(null, c, c2, fuzzyArgs, args);
193   }
194
195   /**
196    * Creates an instance of the specified class from within the context of another object.
197    *
198    * @param outer
199    *    The outer object.
200    *    Can be <jk>null</jk>.
201    * @param c
202    *    The class to cast to.
203    * @param c2
204    *    The class to instantiate.
205    *    Can also be an instance of the class.
206    * @param fuzzyArgs
207    *    Use fuzzy constructor arg matching.
208    *    <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored.
209    *    <br>No-arg constructors are also used if no other constructors are found.
210    * @param args
211    *    The arguments to pass to the constructor.
212    * @return
213    *    The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface.
214    * @throws
215    *    RuntimeException if constructor could not be found or called.
216    */
217   @SuppressWarnings("unchecked")
218   public static <T> T castOrCreateFromOuter(Object outer, Class<T> c, Object c2, boolean fuzzyArgs, Object...args) {
219      if (c2 == null)
220         return null;
221      if (c2 instanceof Class) {
222         try {
223            ClassInfo c3 = ClassInfo.of((Class<?>)c2);
224            if (c3.isInterface() || c3.isAbstract())
225               return null;
226
227            // First look for an exact match.
228            ConstructorInfo con = c3.getPublicConstructor(args);
229            if (con != null)
230               return con.<T>invoke(args);
231
232            // Next look for an exact match including the outer.
233            if (outer != null) {
234               args = new AList<>().append(outer).appendAll(args).toArray();
235               con = c3.getPublicConstructor(args);
236               if (con != null)
237                  return con.<T>invoke(args);
238            }
239
240            // Finally use fuzzy matching.
241            if (fuzzyArgs) {
242               con = c3.getPublicConstructorFuzzy(args);
243               if (con != null)
244                  return con.<T>invoke(getMatchingArgs(con.getParamTypes(), args));
245            }
246
247            throw new FormattedRuntimeException("Could not instantiate class {0}/{1}.  Constructor not found.", c.getName(), c2);
248         } catch (Exception e) {
249            throw new FormattedRuntimeException(e, "Could not instantiate class {0}", c.getName());
250         }
251      } else if (ClassInfo.of(c).isParentOf(c2.getClass())) {
252         return (T)c2;
253      } else {
254         throw new FormattedRuntimeException("Object of type {0} found but was expecting {1}.", c2.getClass(), c.getClass());
255      }
256   }
257
258   /**
259    * Matches arguments to a list of parameter types.
260    *
261    * <p>
262    * Extra parameters are ignored.
263    * <br>Missing parameters are left null.
264    *
265    * @param paramTypes The parameter types.
266    * @param args The arguments to match to the parameter types.
267    * @return
268    *    An array of parameters.
269    */
270   public static Object[] getMatchingArgs(Class<?>[] paramTypes, Object... args) {
271      Object[] params = new Object[paramTypes.length];
272      for (int i = 0; i < paramTypes.length; i++) {
273         ClassInfo pt = ClassInfo.of(paramTypes[i]).getWrapperInfoIfPrimitive();
274         for (int j = 0; j < args.length; j++) {
275            if (args[j] != null && pt.isParentOf(args[j].getClass())) {
276               params[i] = args[j];
277               break;
278            }
279         }
280      }
281      return params;
282   }
283
284   /**
285    * Matches arguments to a list of parameter types.
286    *
287    * <p>
288    * Extra parameters are ignored.
289    * <br>Missing parameters are left null.
290    *
291    * @param paramTypes The parameter types.
292    * @param args The arguments to match to the parameter types.
293    * @return
294    *    An array of parameters.
295    */
296   public static Object[] getMatchingArgs(List<ClassInfo> paramTypes, Object... args) {
297      Object[] params = new Object[paramTypes.size()];
298      for (int i = 0; i < paramTypes.size(); i++) {
299         ClassInfo pt = paramTypes.get(i).getWrapperInfoIfPrimitive();
300         for (int j = 0; j < args.length; j++) {
301            if (pt.isParentOf(args[j].getClass())) {
302               params[i] = args[j];
303               break;
304            }
305         }
306      }
307      return params;
308   }
309
310   /**
311    * Constructs a new instance of the specified class from the specified string.
312    *
313    * <p>
314    * Class must be one of the following:
315    * <ul>
316    *    <li>Have a public constructor that takes in a single <c>String</c> argument.
317    *    <li>Have a static <c>fromString(String)</c> (or related) method.
318    *    <li>Be an <c>enum</c>.
319    * </ul>
320    *
321    * @param c The class.
322    * @param s The string to create the instance from.
323    * @return A new object instance, or <jk>null</jk> if a method for converting the string to an object could not be found.
324    */
325   public static <T> T fromString(Class<T> c, String s) {
326      Mutater<String,T> t = Mutaters.get(String.class, c);
327      return t == null ? null : t.mutate(s);
328   }
329
330   /**
331    * Converts an object to a string.
332    *
333    * <p>
334    * Normally, this is just going to call <c>toString()</c> on the object.
335    * However, the {@link Locale} and {@link TimeZone} objects are treated special so that the returned value
336    * works with the {@link #fromString(Class, String)} method.
337    *
338    * @param o The object to convert to a string.
339    * @return The stringified object, or <jk>null</jk> if the object was <jk>null</jk>.
340    */
341   @SuppressWarnings({ "unchecked" })
342   public static String toString(Object o) {
343      if (o == null)
344         return null;
345      Mutater<Object,String> t = (Mutater<Object,String>)Mutaters.get(o.getClass(), String.class);
346      return t == null ? o.toString() : t.mutate(o);
347   }
348
349   /**
350    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
351    *
352    * @param x The constructor.
353    * @return <jk>true</jk> if call was successful.
354    */
355   public static boolean setAccessible(Constructor<?> x) {
356      try {
357         if (! (x == null || x.isAccessible()))
358            x.setAccessible(true);
359         return true;
360      } catch (SecurityException e) {
361         return false;
362      }
363   }
364
365   /**
366    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
367    *
368    * @param x The method.
369    * @return <jk>true</jk> if call was successful.
370    */
371   public static boolean setAccessible(Method x) {
372      try {
373         if (! (x == null || x.isAccessible()))
374            x.setAccessible(true);
375         return true;
376      } catch (SecurityException e) {
377         return false;
378      }
379   }
380
381   /**
382    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
383    *
384    * @param x The field.
385    * @return <jk>true</jk> if call was successful.
386    */
387   public static boolean setAccessible(Field x) {
388      try {
389         if (! (x == null || x.isAccessible()))
390            x.setAccessible(true);
391         return true;
392      } catch (SecurityException e) {
393         return false;
394      }
395   }
396
397   /**
398    * Returns the specified type as a <c>Class</c>.
399    *
400    * <p>
401    * If it's already a <c>Class</c>, it just does a cast.
402    * <br>If it's a <c>ParameterizedType</c>, it returns the raw type.
403    *
404    * @param t The type to convert.
405    * @return The type converted to a <c>Class</c>, or <jk>null</jk> if it could not be converted.
406    */
407   public static Class<?> toClass(Type t) {
408      if (t instanceof Class)
409         return (Class<?>)t;
410      if (t instanceof ParameterizedType) {
411         ParameterizedType pt = (ParameterizedType)t;
412         // The raw type should always be a class (right?)
413         return (Class<?>)pt.getRawType();
414      }
415      return null;
416   }
417}