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}