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