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}