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}