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; 014 015import static org.apache.juneau.ClassMeta.ClassCategory.*; 016import static org.apache.juneau.internal.ClassFlags.*; 017import static org.apache.juneau.internal.ClassUtils.*; 018 019import java.io.*; 020import java.lang.annotation.*; 021import java.lang.reflect.*; 022import java.lang.reflect.Proxy; 023import java.net.*; 024import java.net.URI; 025import java.util.*; 026import java.util.Date; 027import java.util.concurrent.*; 028import java.util.concurrent.locks.*; 029 030import org.apache.juneau.annotation.*; 031import org.apache.juneau.http.*; 032import org.apache.juneau.internal.*; 033import org.apache.juneau.json.*; 034import org.apache.juneau.parser.*; 035import org.apache.juneau.serializer.*; 036import org.apache.juneau.transform.*; 037import org.apache.juneau.utils.*; 038 039/** 040 * A wrapper class around the {@link Class} object that provides cached information about that class. 041 * 042 * <p> 043 * Instances of this class can be created through the {@link BeanContext#getClassMeta(Class)} method. 044 * 045 * <p> 046 * The {@link BeanContext} class will cache and reuse instances of this class except for the following class types: 047 * <ul> 048 * <li>Arrays 049 * <li>Maps with non-Object key/values. 050 * <li>Collections with non-Object key/values. 051 * </ul> 052 * 053 * <p> 054 * This class is tied to the {@link BeanContext} class because it's that class that makes the determination of what is 055 * a bean. 056 * 057 * @param <T> The class type of the wrapped class. 058 */ 059@Bean(properties="innerClass,classCategory,elementType,keyType,valueType,notABeanReason,initException,beanMeta") 060public final class ClassMeta<T> implements Type { 061 062 /** Class categories. */ 063 enum ClassCategory { 064 MAP, COLLECTION, CLASS, METHOD, NUMBER, DECIMAL, BOOLEAN, CHAR, DATE, ARRAY, ENUM, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM, VOID, ARGS 065 } 066 067 final Class<T> innerClass; // The class being wrapped. 068 069 private final Class<? extends T> implClass; // The implementation class to use if this is an interface. 070 private final ClassCategory cc; // The class category. 071 private final Method fromStringMethod; // The static valueOf(String) or fromString(String) or forString(String) method (if it has one). 072 private final Constructor<? extends T> 073 noArgConstructor; // The no-arg constructor for this class (if it has one). 074 private final Constructor<T> 075 stringConstructor, // The X(String) constructor (if it has one). 076 numberConstructor, // The X(Number) constructor (if it has one). 077 swapConstructor; // The X(Swappable) constructor (if it has one). 078 private final Class<?> 079 swapMethodType, // The class type of the object in the number constructor. 080 numberConstructorType; 081 private final Method 082 swapMethod, // The swap() method (if it has one). 083 unswapMethod, // The unswap() method (if it has one). 084 exampleMethod; // The example() or @Example-annotated method (if it has one). 085 private final Field 086 exampleField; // The @Example-annotated field (if it has one). 087 private final Setter 088 namePropertyMethod, // The method to set the name on an object (if it has one). 089 parentPropertyMethod; // The method to set the parent on an object (if it has one). 090 private final boolean 091 isDelegate, // True if this class extends Delegate. 092 isAbstract, // True if this class is abstract. 093 isMemberClass; // True if this is a non-static member class. 094 private final Object primitiveDefault; // Default value for primitive type classes. 095 private final Map<String,Method> 096 publicMethods; // All public methods, including static methods. 097 private final PojoSwap<?,?>[] childPojoSwaps; // Any PojoSwaps where the normal type is a subclass of this class. 098 private final ConcurrentHashMap<Class<?>,PojoSwap<?,?>> 099 childSwapMap, // Maps normal subclasses to PojoSwaps. 100 childUnswapMap; // Maps swap subclasses to PojoSwaps. 101 private final PojoSwap<T,?>[] pojoSwaps; // The object POJO swaps associated with this bean (if it has any). 102 private final BeanFilter beanFilter; // The bean filter associated with this bean (if it has one). 103 private final BuilderSwap<T,?> builderSwap; // The builder swap associated with this bean (if it has one). 104 private final MetadataMap extMeta; // Extended metadata 105 private final BeanContext beanContext; // The bean context that created this object. 106 private final ClassMeta<?> 107 elementType, // If ARRAY or COLLECTION, the element class type. 108 keyType, // If MAP, the key class type. 109 valueType; // If MAP, the value class type. 110 private final BeanMeta<T> beanMeta; // The bean meta for this bean class (if it's a bean). 111 private final String 112 typePropertyName, // The property name of the _type property for this class and subclasses. 113 notABeanReason, // If this isn't a bean, the reason why. 114 dictionaryName; // The dictionary name of this class if it has one. 115 private final Throwable initException; // Any exceptions thrown in the init() method. 116 private final InvocationHandler invocationHandler; // The invocation handler for this class (if it has one). 117 private final BeanRegistry beanRegistry; // The bean registry of this class meta (if it has one). 118 private final ClassMeta<?>[] args; // Arg types if this is an array of args. 119 private final Object example; // Example object. 120 private final Map<Class<?>,Transform<?,T>> fromTransforms = new ConcurrentHashMap<>(); 121 private final Map<Class<?>,Transform<T,?>> toTransforms = new ConcurrentHashMap<>(); 122 private final Transform<Reader,T> readerTransform; 123 private final Transform<InputStream,T> inputStreamTransform; 124 private final Transform<String,T> stringTransform; 125 126 private ReadWriteLock lock = new ReentrantReadWriteLock(false); 127 private Lock rLock = lock.readLock(), wLock = lock.writeLock(); 128 129 /** 130 * Construct a new {@code ClassMeta} based on the specified {@link Class}. 131 * 132 * @param innerClass The class being wrapped. 133 * @param beanContext The bean context that created this object. 134 * @param implClass 135 * For interfaces and abstract classes, this represents the "real" class to instantiate. 136 * Can be <jk>null</jk>. 137 * @param beanFilter 138 * The {@link BeanFilter} programmatically associated with this class. 139 * Can be <jk>null</jk>. 140 * @param pojoSwap 141 * The {@link PojoSwap} programmatically associated with this class. 142 * Can be <jk>null</jk>. 143 * @param childPojoSwap 144 * The child {@link PojoSwap PojoSwaps} programmatically associated with this class. 145 * These are the <code>PojoSwaps</code> that have normal classes that are subclasses of this class. 146 * Can be <jk>null</jk>. 147 * @param delayedInit 148 * Don't call init() in constructor. 149 * Used for delayed initialization when the possibility of class reference loops exist. 150 */ 151 @SuppressWarnings({ "rawtypes", "unchecked" }) 152 ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[] childPojoSwaps, Object example) { 153 this.innerClass = innerClass; 154 this.beanContext = beanContext; 155 this.extMeta = new MetadataMap(); 156 String notABeanReason = null; 157 158 wLock.lock(); 159 try { 160 // We always immediately add this class meta to the bean context cache so that we can resolve recursive references. 161 if (beanContext != null && beanContext.cmCache != null) 162 beanContext.cmCache.put(innerClass, this); 163 164 ClassMetaBuilder<T> builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwaps, childPojoSwaps, example); 165 166 this.cc = builder.cc; 167 this.isDelegate = builder.isDelegate; 168 this.fromStringMethod = builder.fromStringMethod; 169 this.swapMethod = builder.swapMethod; 170 this.unswapMethod = builder.unswapMethod; 171 this.swapMethodType = builder.swapMethodType; 172 this.parentPropertyMethod = builder.parentPropertyMethod; 173 this.namePropertyMethod = builder.namePropertyMethod; 174 this.noArgConstructor = builder.noArgConstructor; 175 this.stringConstructor = builder.stringConstructor; 176 this.swapConstructor = builder.swapConstructor; 177 this.numberConstructor = builder.numberConstructor; 178 this.numberConstructorType = builder.numberConstructorType; 179 this.primitiveDefault = builder.primitiveDefault; 180 this.publicMethods = builder.publicMethods; 181 this.beanFilter = beanFilter; 182 this.pojoSwaps = builder.pojoSwaps.isEmpty() ? null : builder.pojoSwaps.toArray(new PojoSwap[builder.pojoSwaps.size()]); 183 this.builderSwap = builder.builderSwap; 184 this.keyType = builder.keyType; 185 this.valueType = builder.valueType; 186 this.elementType = builder.elementType; 187 notABeanReason = builder.notABeanReason; 188 this.beanMeta = builder.beanMeta; 189 this.initException = builder.initException; 190 this.typePropertyName = builder.typePropertyName; 191 this.dictionaryName = builder.dictionaryName; 192 this.invocationHandler = builder.invocationHandler; 193 this.beanRegistry = builder.beanRegistry; 194 this.isMemberClass = builder.isMemberClass; 195 this.isAbstract = builder.isAbstract; 196 this.implClass = builder.implClass; 197 this.childUnswapMap = builder.childUnswapMap; 198 this.childSwapMap = builder.childSwapMap; 199 this.childPojoSwaps = builder.childPojoSwaps; 200 this.exampleMethod = builder.exampleMethod; 201 this.exampleField = builder.exampleField; 202 this.example = builder.example; 203 this.args = null; 204 this.readerTransform = builder.readerTransform; 205 this.inputStreamTransform = builder.inputStreamTransform; 206 this.stringTransform = builder.stringTransform; 207 } catch (ClassMetaRuntimeException e) { 208 notABeanReason = e.getMessage(); 209 throw e; 210 } finally { 211 this.notABeanReason = notABeanReason; 212 wLock.unlock(); 213 } 214 } 215 216 /** 217 * Causes thread to wait until constructor has exited. 218 */ 219 final void waitForInit() { 220 rLock.lock(); 221 rLock.unlock(); 222 } 223 224 /** 225 * Copy constructor. 226 * 227 * <p> 228 * Used for creating Map and Collection class metas that shouldn't be cached. 229 */ 230 ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> valueType, ClassMeta<?> elementType) { 231 this.innerClass = mainType.innerClass; 232 this.implClass = mainType.implClass; 233 this.childPojoSwaps = mainType.childPojoSwaps; 234 this.childSwapMap = mainType.childSwapMap; 235 this.childUnswapMap = mainType.childUnswapMap; 236 this.cc = mainType.cc; 237 this.fromStringMethod = mainType.fromStringMethod; 238 this.noArgConstructor = mainType.noArgConstructor; 239 this.stringConstructor = mainType.stringConstructor; 240 this.numberConstructor = mainType.numberConstructor; 241 this.swapConstructor = mainType.swapConstructor; 242 this.swapMethodType = mainType.swapMethodType; 243 this.numberConstructorType = mainType.numberConstructorType; 244 this.swapMethod = mainType.swapMethod; 245 this.unswapMethod = mainType.unswapMethod; 246 this.namePropertyMethod = mainType.namePropertyMethod; 247 this.parentPropertyMethod = mainType.parentPropertyMethod; 248 this.isDelegate = mainType.isDelegate; 249 this.isAbstract = mainType.isAbstract; 250 this.isMemberClass = mainType.isMemberClass; 251 this.primitiveDefault = mainType.primitiveDefault; 252 this.publicMethods = mainType.publicMethods; 253 this.beanContext = mainType.beanContext; 254 this.elementType = elementType; 255 this.keyType = keyType; 256 this.valueType = valueType; 257 this.invocationHandler = mainType.invocationHandler; 258 this.beanMeta = mainType.beanMeta; 259 this.typePropertyName = mainType.typePropertyName; 260 this.dictionaryName = mainType.dictionaryName; 261 this.notABeanReason = mainType.notABeanReason; 262 this.pojoSwaps = mainType.pojoSwaps; 263 this.builderSwap = mainType.builderSwap; 264 this.beanFilter = mainType.beanFilter; 265 this.extMeta = mainType.extMeta; 266 this.initException = mainType.initException; 267 this.beanRegistry = mainType.beanRegistry; 268 this.exampleMethod = mainType.exampleMethod; 269 this.exampleField = mainType.exampleField; 270 this.example = mainType.example; 271 this.args = null; 272 this.readerTransform = mainType.readerTransform; 273 this.inputStreamTransform = mainType.inputStreamTransform; 274 this.stringTransform = mainType.stringTransform; 275 } 276 277 /** 278 * Constructor for args-arrays. 279 */ 280 @SuppressWarnings("unchecked") 281 ClassMeta(ClassMeta<?>[] args) { 282 this.innerClass = (Class<T>) Object[].class; 283 this.extMeta = new MetadataMap(); 284 this.args = args; 285 this.implClass = null; 286 this.childPojoSwaps = null; 287 this.childSwapMap = null; 288 this.childUnswapMap = null; 289 this.cc = ARGS; 290 this.fromStringMethod = null; 291 this.noArgConstructor = null; 292 this.stringConstructor = null; 293 this.numberConstructor = null; 294 this.swapConstructor = null; 295 this.swapMethodType = null; 296 this.numberConstructorType = null; 297 this.swapMethod = null; 298 this.unswapMethod = null; 299 this.namePropertyMethod = null; 300 this.parentPropertyMethod = null; 301 this.isDelegate = false; 302 this.isAbstract = false; 303 this.isMemberClass = false; 304 this.primitiveDefault = null; 305 this.publicMethods = null; 306 this.beanContext = null; 307 this.elementType = null; 308 this.keyType = null; 309 this.valueType = null; 310 this.invocationHandler = null; 311 this.beanMeta = null; 312 this.typePropertyName = null; 313 this.dictionaryName = null; 314 this.notABeanReason = null; 315 this.pojoSwaps = null; 316 this.builderSwap = null; 317 this.beanFilter = null; 318 this.initException = null; 319 this.beanRegistry = null; 320 this.exampleMethod = null; 321 this.exampleField = null; 322 this.example = null; 323 this.readerTransform = null; 324 this.inputStreamTransform = null; 325 this.stringTransform = null; 326 } 327 328 @SuppressWarnings({"unchecked","rawtypes","hiding"}) 329 private final class ClassMetaBuilder<T> { 330 Class<T> innerClass; 331 Class<? extends T> implClass; 332 BeanContext beanContext; 333 ClassCategory cc = ClassCategory.OTHER; 334 boolean 335 isDelegate = false, 336 isMemberClass = false, 337 isAbstract = false; 338 Method 339 fromStringMethod = null, 340 swapMethod = null, 341 unswapMethod = null; 342 Setter 343 parentPropertyMethod = null, 344 namePropertyMethod = null; 345 Constructor<T> 346 noArgConstructor = null, 347 stringConstructor = null, 348 swapConstructor = null, 349 numberConstructor = null; 350 Class<?> 351 swapMethodType = null, 352 numberConstructorType = null; 353 Object primitiveDefault = null; 354 Map<String,Method> 355 publicMethods = new LinkedHashMap<>(); 356 ClassMeta<?> 357 keyType = null, 358 valueType = null, 359 elementType = null; 360 String 361 typePropertyName = null, 362 notABeanReason = null, 363 dictionaryName = null; 364 Throwable initException = null; 365 BeanMeta beanMeta = null; 366 List<PojoSwap> pojoSwaps = new ArrayList<>(); 367 BuilderSwap builderSwap; 368 InvocationHandler invocationHandler = null; 369 BeanRegistry beanRegistry = null; 370 PojoSwap<?,?>[] childPojoSwaps; 371 ConcurrentHashMap<Class<?>,PojoSwap<?,?>> 372 childSwapMap, 373 childUnswapMap; 374 Method exampleMethod; 375 Field exampleField; 376 Object example; 377 Transform<Reader,T> readerTransform; 378 Transform<InputStream,T> inputStreamTransform; 379 Transform<String,T> stringTransform; 380 381 ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[] childPojoSwaps, Object example) { 382 this.innerClass = innerClass; 383 this.beanContext = beanContext; 384 385 this.implClass = implClass; 386 this.childPojoSwaps = childPojoSwaps; 387 if (childPojoSwaps == null) { 388 this.childSwapMap = null; 389 this.childUnswapMap = null; 390 } else { 391 this.childSwapMap = new ConcurrentHashMap<>(); 392 this.childUnswapMap = new ConcurrentHashMap<>(); 393 } 394 395 Class<T> c = innerClass; 396 if (c.isPrimitive()) { 397 if (c == Boolean.TYPE) 398 cc = BOOLEAN; 399 else if (c == Byte.TYPE || c == Short.TYPE || c == Integer.TYPE || c == Long.TYPE || c == Float.TYPE || c == Double.TYPE) { 400 if (c == Float.TYPE || c == Double.TYPE) 401 cc = DECIMAL; 402 else 403 cc = NUMBER; 404 } 405 else if (c == Character.TYPE) 406 cc = CHAR; 407 else if (c == void.class || c == Void.class) 408 cc = VOID; 409 } else { 410 if (isParentClass(Delegate.class, c)) 411 isDelegate = true; 412 413 if (c == Object.class) 414 cc = OBJ; 415 else if (c.isEnum()) 416 cc = ENUM; 417 else if (c.equals(Class.class)) 418 cc = CLASS; 419 else if (isParentClass(Method.class, c)) 420 cc = METHOD; 421 else if (isParentClass(CharSequence.class, c)) { 422 if (c.equals(String.class)) 423 cc = STR; 424 else 425 cc = CHARSEQ; 426 } 427 else if (isParentClass(Number.class, c)) { 428 if (isParentClass(Float.class, c) || isParentClass(Double.class, c)) 429 cc = DECIMAL; 430 else 431 cc = NUMBER; 432 } 433 else if (isParentClass(Collection.class, c)) 434 cc = COLLECTION; 435 else if (isParentClass(Map.class, c)) { 436 if (isParentClass(BeanMap.class, c)) 437 cc = BEANMAP; 438 else 439 cc = MAP; 440 } 441 else if (c == Character.class) 442 cc = CHAR; 443 else if (c == Boolean.class) 444 cc = BOOLEAN; 445 else if (isParentClass(Date.class, c) || isParentClass(Calendar.class, c)) 446 cc = DATE; 447 else if (c.isArray()) 448 cc = ARRAY; 449 else if (isParentClass(URL.class, c) || isParentClass(URI.class, c) || c.isAnnotationPresent(org.apache.juneau.annotation.URI.class)) 450 cc = URI; 451 else if (isParentClass(Reader.class, c)) 452 cc = READER; 453 else if (isParentClass(InputStream.class, c)) 454 cc = INPUTSTREAM; 455 } 456 457 isMemberClass = c.isMemberClass() && ! isStatic(c); 458 459 // Find static fromString(String) or equivalent method. 460 // fromString() must be checked before valueOf() so that Enum classes can create their own 461 // specialized fromString() methods to override the behavior of Enum.valueOf(String). 462 // valueOf() is used by enums. 463 // parse() is used by the java logging Level class. 464 // forName() is used by Class and Charset 465 for (String methodName : new String[]{"fromString","fromValue","valueOf","parse","parseString","forName","forString"}) { 466 if (fromStringMethod == null) { 467 for (Method m : c.getMethods()) { 468 if (isAll(m, STATIC, PUBLIC, NOT_DEPRECATED) && hasName(m, methodName) && hasReturnType(m, c) && hasArgs(m, String.class)) { 469 fromStringMethod = m; 470 break; 471 } 472 } 473 } 474 } 475 // TODO - should use transforms for above code. 476 477 // Special cases 478 try { 479 if (c == TimeZone.class) 480 fromStringMethod = c.getMethod("getTimeZone", String.class); 481 else if (c == Locale.class) 482 fromStringMethod = LocaleAsString.class.getMethod("fromString", String.class); 483 } catch (NoSuchMethodException e1) {} 484 485 // Find swap() method if present. 486 for (Method m : c.getMethods()) { 487 if (isAll(m, PUBLIC, NOT_DEPRECATED, NOT_STATIC) && hasName(m, "swap") && hasFuzzyArgs(m, BeanSession.class)) { 488 swapMethod = m; 489 swapMethodType = m.getReturnType(); 490 break; 491 } 492 } 493 // Find unswap() method if present. 494 if (swapMethod != null) { 495 for (Method m : c.getMethods()) { 496 if (isAll(m, PUBLIC, NOT_DEPRECATED, STATIC) &&hasName(m, "unswap") && hasFuzzyArgs(m, BeanSession.class, swapMethodType)) { 497 unswapMethod = m; 498 break; 499 } 500 } 501 } 502 503 // Find example() method if present. 504 for (Method m : c.getMethods()) { 505 if (isAll(m, PUBLIC, NOT_DEPRECATED, STATIC) && hasName(m, "example") && hasFuzzyArgs(m, BeanSession.class)) { 506 exampleMethod = m; 507 break; 508 } 509 } 510 511 for (Field f : getAllFields(c, true)) { 512 if (f.isAnnotationPresent(ParentProperty.class)) { 513 if (isStatic(f)) 514 throw new ClassMetaRuntimeException("@ParentProperty used on invalid field ''{0}''. Must be static.", f); 515 setAccessible(f, false); 516 parentPropertyMethod = new Setter.FieldSetter(f); 517 } 518 if (f.isAnnotationPresent(NameProperty.class)) { 519 if (isStatic(f)) 520 throw new ClassMetaRuntimeException("@NameProperty used on invalid field ''{0}''. Must be static.", f); 521 setAccessible(f, false); 522 namePropertyMethod = new Setter.FieldSetter(f); 523 } 524 } 525 526 for (Field f : c.getDeclaredFields()) { 527 if (f.isAnnotationPresent(Example.class)) { 528 if (! (isStatic(f) && isParentClass(innerClass, f.getType()))) 529 throw new ClassMetaRuntimeException("@Example used on invalid field ''{0}''. Must be static and an instance of the type.", f); 530 setAccessible(f, false); 531 exampleField = f; 532 } 533 } 534 535 // Find @NameProperty and @ParentProperty methods if present. 536 for (Method m : getAllMethods(c, true)) { 537 if (m.isAnnotationPresent(ParentProperty.class)) { 538 if (isStatic(m) || ! hasNumArgs(m, 1)) 539 throw new ClassMetaRuntimeException("@ParentProperty used on invalid method ''{0}''. Must not be static and have one argument.", m); 540 setAccessible(m, false); 541 parentPropertyMethod = new Setter.MethodSetter(m); 542 } 543 if (m.isAnnotationPresent(NameProperty.class)) { 544 if (isStatic(m) || ! hasNumArgs(m, 1)) 545 throw new ClassMetaRuntimeException("@NameProperty used on invalid method ''{0}''. Must not be static and have one argument.", m); 546 setAccessible(m, false); 547 namePropertyMethod = new Setter.MethodSetter(m); 548 } 549 } 550 551 for (Method m : c.getDeclaredMethods()) { 552 if (m.isAnnotationPresent(Example.class)) { 553 if (! (isStatic(m) && hasFuzzyArgs(m, BeanSession.class) && isParentClass(innerClass, m.getReturnType()))) 554 throw new ClassMetaRuntimeException("@Example used on invalid method ''{0}''. Must be static and return an instance of the declaring class.", m); 555 setAccessible(m, false); 556 exampleMethod = m; 557 } 558 } 559 560 // Note: Primitive types are normally abstract. 561 isAbstract = ClassUtils.isAbstract(c) && ! c.isPrimitive(); 562 563 // Find constructor(String) method if present. 564 for (Constructor cs : c.getConstructors()) { 565 if (isPublic(cs) && isNotDeprecated(cs)) { 566 Class<?>[] pt = cs.getParameterTypes(); 567 if (pt.length == (isMemberClass ? 1 : 0) && c != Object.class && ! isAbstract) { 568 noArgConstructor = cs; 569 } else if (pt.length == (isMemberClass ? 2 : 1)) { 570 Class<?> arg = pt[(isMemberClass ? 1 : 0)]; 571 if (arg == String.class) 572 stringConstructor = cs; 573 else if (swapMethodType != null && swapMethodType.isAssignableFrom(arg)) 574 swapConstructor = cs; 575 else if (cc != NUMBER && (Number.class.isAssignableFrom(arg) || (arg.isPrimitive() && (arg == int.class || arg == short.class || arg == long.class || arg == float.class || arg == double.class)))) { 576 numberConstructor = cs; 577 numberConstructorType = getWrapperIfPrimitive(arg); 578 } 579 } 580 } 581 } 582 583 primitiveDefault = ClassUtils.getPrimitiveDefault(c); 584 585 for (Method m : c.getMethods()) 586 if (isAll(m, PUBLIC, NOT_DEPRECATED)) 587 publicMethods.put(getMethodSignature(m), m); 588 589 if (innerClass != Object.class) { 590 noArgConstructor = (Constructor<T>)findNoArgConstructor(implClass == null ? innerClass : implClass, Visibility.PUBLIC); 591 } 592 593 if (beanFilter == null) 594 beanFilter = findBeanFilter(); 595 596 if (swapMethod != null) { 597 final Method fSwapMethod = swapMethod; 598 final Method fUnswapMethod = unswapMethod; 599 final Constructor<T> fSwapConstructor = swapConstructor; 600 this.pojoSwaps.add( 601 new PojoSwap<T,Object>(c, swapMethod.getReturnType()) { 602 @Override 603 public Object swap(BeanSession session, Object o) throws SerializeException { 604 try { 605 return fSwapMethod.invoke(o, getMatchingArgs(fSwapMethod.getParameterTypes(), session)); 606 } catch (Exception e) { 607 throw new SerializeException(e); 608 } 609 } 610 @Override 611 public T unswap(BeanSession session, Object f, ClassMeta<?> hint) throws ParseException { 612 try { 613 if (fUnswapMethod != null) 614 return (T)fUnswapMethod.invoke(null, getMatchingArgs(fSwapMethod.getParameterTypes(), session, f)); 615 if (fSwapConstructor != null) 616 return fSwapConstructor.newInstance(f); 617 return super.unswap(session, f, hint); 618 } catch (Exception e) { 619 throw new ParseException(e); 620 } 621 } 622 } 623 ); 624 } 625 626 if (pojoSwaps != null) 627 this.pojoSwaps.addAll(Arrays.asList(pojoSwaps)); 628 629 if (beanContext != null) 630 this.builderSwap = BuilderSwap.findSwapFromPojoClass(c, beanContext.getBeanConstructorVisibility(), beanContext.getBeanMethodVisibility()); 631 632 findPojoSwaps(this.pojoSwaps); 633 634 try { 635 636 // If this is an array, get the element type. 637 if (cc == ARRAY) 638 elementType = findClassMeta(innerClass.getComponentType()); 639 640 // If this is a MAP, see if it's parameterized (e.g. AddressBook extends HashMap<String,Person>) 641 else if (cc == MAP) { 642 ClassMeta[] parameters = findParameters(); 643 if (parameters != null && parameters.length == 2) { 644 keyType = parameters[0]; 645 valueType = parameters[1]; 646 } else { 647 keyType = findClassMeta(Object.class); 648 valueType = findClassMeta(Object.class); 649 } 650 } 651 652 // If this is a COLLECTION, see if it's parameterized (e.g. AddressBook extends LinkedList<Person>) 653 else if (cc == COLLECTION) { 654 ClassMeta[] parameters = findParameters(); 655 if (parameters != null && parameters.length == 1) { 656 elementType = parameters[0]; 657 } else { 658 elementType = findClassMeta(Object.class); 659 } 660 } 661 662 // If the category is unknown, see if it's a bean. 663 // Note that this needs to be done after all other initialization has been done. 664 else if (cc == OTHER) { 665 666 BeanMeta newMeta = null; 667 try { 668 newMeta = new BeanMeta(ClassMeta.this, beanContext, beanFilter, null); 669 notABeanReason = newMeta.notABeanReason; 670 671 // Always get these even if it's not a bean: 672 beanRegistry = newMeta.beanRegistry; 673 typePropertyName = newMeta.typePropertyName; 674 675 } catch (RuntimeException e) { 676 notABeanReason = e.getMessage(); 677 throw e; 678 } 679 if (notABeanReason == null) 680 beanMeta = newMeta; 681 } 682 683 } catch (NoClassDefFoundError e) { 684 initException = e; 685 } catch (RuntimeException e) { 686 initException = e; 687 throw e; 688 } 689 690 if (beanMeta != null) 691 dictionaryName = beanMeta.getDictionaryName(); 692 693 if (beanMeta != null && beanContext != null && beanContext.isUseInterfaceProxies() && innerClass.isInterface()) 694 invocationHandler = new BeanProxyInvocationHandler<T>(beanMeta); 695 696 Bean b = c.getAnnotation(Bean.class); 697 if (b != null) { 698 if (b.beanDictionary().length != 0) 699 beanRegistry = new BeanRegistry(beanContext, null, b.beanDictionary()); 700 701 // This could be a non-bean POJO with a type name. 702 if (dictionaryName == null && ! b.typeName().isEmpty()) 703 dictionaryName = b.typeName(); 704 } 705 706 Example e = c.getAnnotation(Example.class); 707 708 if (example == null && e != null && ! e.value().isEmpty()) 709 example = e.value(); 710 711 if (example == null) { 712 switch(cc) { 713 case BOOLEAN: 714 example = true; 715 break; 716 case CHAR: 717 example = 'a'; 718 break; 719 case CHARSEQ: 720 case STR: 721 example = "foo"; 722 break; 723 case DECIMAL: 724 if (isFloat()) 725 example = new Float(1f); 726 else if (isDouble()) 727 example = new Double(1d); 728 break; 729 case ENUM: 730 Iterator<? extends Enum> i = EnumSet.allOf((Class<? extends Enum>)c).iterator(); 731 if (i.hasNext()) 732 example = i.next(); 733 break; 734 case NUMBER: 735 if (isShort()) 736 example = new Short((short)1); 737 else if (isInteger()) 738 example = new Integer(1); 739 else if (isLong()) 740 example = new Long(1l); 741 break; 742 case URI: 743 case ARGS: 744 case ARRAY: 745 case BEANMAP: 746 case CLASS: 747 case COLLECTION: 748 case DATE: 749 case INPUTSTREAM: 750 case MAP: 751 case METHOD: 752 case OBJ: 753 case OTHER: 754 case READER: 755 case VOID: 756 break; 757 } 758 } 759 760 this.example = example; 761 762 this.readerTransform = TransformCache.get(Reader.class, c); 763 this.inputStreamTransform = TransformCache.get(InputStream.class, c); 764 this.stringTransform = TransformCache.get(String.class, c); 765 } 766 767 private BeanFilter findBeanFilter() { 768 try { 769 Map<Class<?>,Bean> ba = getAnnotationsMap(Bean.class, innerClass); 770 if (! ba.isEmpty()) 771 return new AnnotationBeanFilterBuilder(innerClass, ba).build(); 772 } catch (Exception e) { 773 throw new RuntimeException(e); 774 } 775 return null; 776 } 777 778 private void findPojoSwaps(List<PojoSwap> l) { 779 Swap swap = innerClass.getAnnotation(Swap.class); 780 if (swap != null) 781 l.add(createPojoSwap(swap)); 782 Swaps swaps = innerClass.getAnnotation(Swaps.class); 783 if (swaps != null) 784 for (Swap s : swaps.value()) 785 l.add(createPojoSwap(s)); 786 } 787 788 private PojoSwap<T,?> createPojoSwap(Swap s) { 789 Class<?> c = s.value(); 790 if (c == Null.class) 791 c = s.impl(); 792 793 if (isParentClass(PojoSwap.class, c)) { 794 PojoSwap ps = beanContext.newInstance(PojoSwap.class, c); 795 if (s.mediaTypes().length > 0) 796 ps.forMediaTypes(MediaType.forStrings(s.mediaTypes())); 797 if (! s.template().isEmpty()) 798 ps.withTemplate(s.template()); 799 return ps; 800 } 801 802 if (isParentClass(Surrogate.class, c)) { 803 List<SurrogateSwap<?,?>> l = SurrogateSwap.findPojoSwaps(c); 804 if (! l.isEmpty()) 805 return (PojoSwap<T,?>)l.iterator().next(); 806 } 807 808 throw new ClassMetaRuntimeException("Invalid swap class ''{0}'' specified. Must extend from PojoSwap or Surrogate.", c); 809 } 810 811 private ClassMeta<?> findClassMeta(Class<?> c) { 812 return beanContext.getClassMeta(c, false); 813 } 814 815 private ClassMeta<?>[] findParameters() { 816 return beanContext.findParameters(innerClass, innerClass); 817 } 818 } 819 820 821 /** 822 * Returns the type property name associated with this class and subclasses. 823 * 824 * <p> 825 * If <jk>null</jk>, <js>"_type"</js> should be assumed. 826 * 827 * @return 828 * The type property name associated with this bean class, or <jk>null</jk> if there is no explicit type 829 * property name defined or this isn't a bean. 830 */ 831 public String getBeanTypePropertyName() { 832 return typePropertyName; 833 } 834 835 /** 836 * Returns the bean dictionary name associated with this class. 837 * 838 * <p> 839 * The lexical name is defined by {@link Bean#typeName() @Bean(typeName)}. 840 * 841 * @return 842 * The type name associated with this bean class, or <jk>null</jk> if there is no type name defined or this 843 * isn't a bean. 844 */ 845 public String getDictionaryName() { 846 return dictionaryName; 847 } 848 849 /** 850 * Returns the bean registry for this class. 851 * 852 * <p> 853 * This bean registry contains names specified in the {@link Bean#beanDictionary() @Bean(beanDictionary)} annotation 854 * defined on the class, regardless of whether the class is an actual bean. 855 * This allows interfaces to define subclasses with type names. 856 * 857 * @return The bean registry for this class, or <jk>null</jk> if no bean registry is associated with it. 858 */ 859 public BeanRegistry getBeanRegistry() { 860 return beanRegistry; 861 } 862 863 /** 864 * Returns the category of this class. 865 * 866 * @return The category of this class. 867 */ 868 public ClassCategory getClassCategory() { 869 return cc; 870 } 871 872 /** 873 * Returns <jk>true</jk> if this class is a superclass of or the same as the specified class. 874 * 875 * @param c The comparison class. 876 * @return <jk>true</jk> if this class is a superclass of or the same as the specified class. 877 */ 878 public boolean isAssignableFrom(Class<?> c) { 879 return isParentClass(innerClass, c); 880 } 881 882 /** 883 * Returns <jk>true</jk> if this class is a subclass of or the same as the specified class. 884 * 885 * @param c The comparison class. 886 * @return <jk>true</jk> if this class is a subclass of or the same as the specified class. 887 */ 888 public boolean isInstanceOf(Class<?> c) { 889 return isParentClass(c, innerClass); 890 } 891 892 /** 893 * Returns <jk>true</jk> if this class or any child classes has a {@link PojoSwap} associated with it. 894 * 895 * <p> 896 * Used when transforming bean properties to prevent having to look up transforms if we know for certain that no 897 * transforms are associated with a bean property. 898 * 899 * @return <jk>true</jk> if this class or any child classes has a {@link PojoSwap} associated with it. 900 */ 901 protected boolean hasChildPojoSwaps() { 902 return childPojoSwaps != null; 903 } 904 905 /** 906 * Returns the {@link PojoSwap} where the specified class is the same/subclass of the normal class of one of the 907 * child POJO swaps associated with this class. 908 * 909 * @param normalClass The normal class being resolved. 910 * @return The resolved {@link PojoSwap} or <jk>null</jk> if none were found. 911 */ 912 protected PojoSwap<?,?> getChildPojoSwapForSwap(Class<?> normalClass) { 913 if (childSwapMap != null) { 914 PojoSwap<?,?> s = childSwapMap.get(normalClass); 915 if (s == null) { 916 for (PojoSwap<?,?> f : childPojoSwaps) 917 if (s == null && isParentClass(f.getNormalClass(), normalClass)) 918 s = f; 919 if (s == null) 920 s = PojoSwap.NULL; 921 PojoSwap<?,?> s2 = childSwapMap.putIfAbsent(normalClass, s); 922 if (s2 != null) 923 s = s2; 924 } 925 if (s == PojoSwap.NULL) 926 return null; 927 return s; 928 } 929 return null; 930 } 931 932 /** 933 * Returns the {@link PojoSwap} where the specified class is the same/subclass of the swap class of one of the child 934 * POJO swaps associated with this class. 935 * 936 * @param swapClass The swap class being resolved. 937 * @return The resolved {@link PojoSwap} or <jk>null</jk> if none were found. 938 */ 939 protected PojoSwap<?,?> getChildPojoSwapForUnswap(Class<?> swapClass) { 940 if (childUnswapMap != null) { 941 PojoSwap<?,?> s = childUnswapMap.get(swapClass); 942 if (s == null) { 943 for (PojoSwap<?,?> f : childPojoSwaps) 944 if (s == null && isParentClass(f.getSwapClass(), swapClass)) 945 s = f; 946 if (s == null) 947 s = PojoSwap.NULL; 948 PojoSwap<?,?> s2 = childUnswapMap.putIfAbsent(swapClass, s); 949 if (s2 != null) 950 s = s2; 951 } 952 if (s == PojoSwap.NULL) 953 return null; 954 return s; 955 } 956 return null; 957 } 958 959 /** 960 * Locates the no-arg constructor for the specified class. 961 * 962 * <p> 963 * Constructor must match the visibility requirements specified by parameter 'v'. 964 * If class is abstract, always returns <jk>null</jk>. 965 * Note that this also returns the 1-arg constructor for non-static member classes. 966 * 967 * @param c The class from which to locate the no-arg constructor. 968 * @param v The minimum visibility. 969 * @return The constructor, or <jk>null</jk> if no no-arg constructor exists with the required visibility. 970 */ 971 @SuppressWarnings({"rawtypes","unchecked"}) 972 protected static <T> Constructor<? extends T> findNoArgConstructor(Class<?> c, Visibility v) { 973 if (ClassUtils.isAbstract(c)) 974 return null; 975 boolean isMemberClass = c.isMemberClass() && ! isStatic(c); 976 for (Constructor cc : c.getConstructors()) { 977 if (hasNumArgs(cc, isMemberClass ? 1 : 0) && v.isVisible(cc.getModifiers()) && isNotDeprecated(cc)) 978 return v.transform(cc); 979 } 980 return null; 981 } 982 983 /** 984 * Returns the {@link Class} object that this class type wraps. 985 * 986 * @return The wrapped class object. 987 */ 988 public Class<T> getInnerClass() { 989 return innerClass; 990 } 991 992 /** 993 * Returns the serialized (swapped) form of this class if there is an {@link PojoSwap} associated with it. 994 * 995 * @param session 996 * The bean session. 997 * <br>Required because the swap used may depend on the media type being serialized or parsed. 998 * @return The serialized class type, or this object if no swap is associated with the class. 999 */ 1000 @BeanIgnore 1001 public ClassMeta<?> getSerializedClassMeta(BeanSession session) { 1002 PojoSwap<T,?> ps = getPojoSwap(session); 1003 return (ps == null ? this : ps.getSwapClassMeta(session)); 1004 } 1005 1006 /** 1007 * Returns the example of this class. 1008 * 1009 * @param session 1010 * The bean session. 1011 * <br>Required because the example method may take it in as a parameter. 1012 * @return The serialized class type, or this object if no swap is associated with the class. 1013 */ 1014 @SuppressWarnings({"unchecked","rawtypes"}) 1015 @BeanIgnore 1016 public T getExample(BeanSession session) { 1017 try { 1018 if (example != null) { 1019 if (isInstance(example)) 1020 return (T)example; 1021 if (example instanceof String) { 1022 if (isCharSequence()) 1023 return (T)example; 1024 String s = example.toString(); 1025 if (isMapOrBean() && StringUtils.isObjectMap(s, false)) 1026 return JsonParser.DEFAULT.parse(s, this); 1027 if (isCollectionOrArray() && StringUtils.isObjectList(s, false)) 1028 return JsonParser.DEFAULT.parse(s, this); 1029 } 1030 if (example instanceof Map && isMapOrBean()) { 1031 return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT_READABLE.toString(example), this); 1032 } 1033 if (example instanceof Collection && isCollectionOrArray()) { 1034 return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT_READABLE.serialize(example), this); 1035 } 1036 } 1037 if (exampleMethod != null) 1038 return (T)invokeMethodFuzzy(exampleMethod, null, session); 1039 if (exampleField != null) 1040 return (T)exampleField.get(null); 1041 1042 if (isCollection()) { 1043 Object etExample = getElementType().getExample(session); 1044 if (etExample != null) { 1045 if (canCreateNewInstance()) { 1046 Collection c = (Collection)newInstance(); 1047 c.add(etExample); 1048 return (T)c; 1049 } 1050 return (T)Collections.singleton(etExample); 1051 } 1052 } else if (isArray()) { 1053 Object etExample = getElementType().getExample(session); 1054 if (etExample != null) { 1055 Object o = Array.newInstance(getElementType().innerClass, 1); 1056 Array.set(o, 0, etExample); 1057 return (T)o; 1058 } 1059 } else if (isMap()) { 1060 Object vtExample = getValueType().getExample(session); 1061 Object ktExample = getKeyType().getExample(session); 1062 if (ktExample != null && vtExample != null) { 1063 if (canCreateNewInstance()) { 1064 Map m = (Map)newInstance(); 1065 m.put(ktExample, vtExample); 1066 return (T)m; 1067 } 1068 return (T)Collections.singletonMap(ktExample, vtExample); 1069 } 1070 } 1071 1072 return null; 1073 } catch (Exception e) { 1074 throw new ClassMetaRuntimeException(e); 1075 } 1076 } 1077 1078 /** 1079 * For array and {@code Collection} types, returns the class type of the components of the array or 1080 * {@code Collection}. 1081 * 1082 * @return The element class type, or <jk>null</jk> if this class is not an array or Collection. 1083 */ 1084 public ClassMeta<?> getElementType() { 1085 return elementType; 1086 } 1087 1088 /** 1089 * For {@code Map} types, returns the class type of the keys of the {@code Map}. 1090 * 1091 * @return The key class type, or <jk>null</jk> if this class is not a Map. 1092 */ 1093 public ClassMeta<?> getKeyType() { 1094 return keyType; 1095 } 1096 1097 /** 1098 * For {@code Map} types, returns the class type of the values of the {@code Map}. 1099 * 1100 * @return The value class type, or <jk>null</jk> if this class is not a Map. 1101 */ 1102 public ClassMeta<?> getValueType() { 1103 return valueType; 1104 } 1105 1106 /** 1107 * Returns <jk>true</jk> if this class implements {@link Delegate}, meaning it's a representation of some other 1108 * object. 1109 * 1110 * @return <jk>true</jk> if this class implements {@link Delegate}. 1111 */ 1112 public boolean isDelegate() { 1113 return isDelegate; 1114 } 1115 1116 /** 1117 * Returns <jk>true</jk> if this class is a subclass of {@link Map}. 1118 * 1119 * @return <jk>true</jk> if this class is a subclass of {@link Map}. 1120 */ 1121 public boolean isMap() { 1122 return cc == MAP || cc == BEANMAP; 1123 } 1124 1125 /** 1126 * Returns <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean. 1127 * 1128 * @return <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean. 1129 */ 1130 public boolean isMapOrBean() { 1131 return cc == MAP || cc == BEANMAP || beanMeta != null; 1132 } 1133 1134 /** 1135 * Returns <jk>true</jk> if this class is a subclass of {@link BeanMap}. 1136 * 1137 * @return <jk>true</jk> if this class is a subclass of {@link BeanMap}. 1138 */ 1139 public boolean isBeanMap() { 1140 return cc == BEANMAP; 1141 } 1142 1143 /** 1144 * Returns <jk>true</jk> if this class is a subclass of {@link Collection}. 1145 * 1146 * @return <jk>true</jk> if this class is a subclass of {@link Collection}. 1147 */ 1148 public boolean isCollection() { 1149 return cc == COLLECTION; 1150 } 1151 1152 /** 1153 * Returns <jk>true</jk> if this class is a subclass of {@link Collection} or is an array. 1154 * 1155 * @return <jk>true</jk> if this class is a subclass of {@link Collection} or is an array. 1156 */ 1157 public boolean isCollectionOrArray() { 1158 return cc == COLLECTION || cc == ARRAY; 1159 } 1160 1161 1162 /** 1163 * Returns <jk>true</jk> if this class extends from {@link Set}. 1164 * 1165 * @return <jk>true</jk> if this class extends from {@link Set}. 1166 */ 1167 public boolean isSet() { 1168 return cc == COLLECTION && isParentClass(Set.class, innerClass); 1169 } 1170 1171 /** 1172 * Returns <jk>true</jk> if this class extends from {@link List}. 1173 * 1174 * @return <jk>true</jk> if this class extends from {@link List}. 1175 */ 1176 public boolean isList() { 1177 return cc == COLLECTION && isParentClass(List.class, innerClass); 1178 } 1179 1180 /** 1181 * Returns <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>. 1182 * 1183 * @return <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>. 1184 */ 1185 public boolean isByteArray() { 1186 return cc == ARRAY && this.innerClass == byte[].class; 1187 } 1188 1189 /** 1190 * Returns <jk>true</jk> if this class is {@link Class}. 1191 * 1192 * @return <jk>true</jk> if this class is {@link Class}. 1193 */ 1194 public boolean isClass() { 1195 return cc == CLASS; 1196 } 1197 1198 /** 1199 * Returns <jk>true</jk> if this class is {@link Method}. 1200 * 1201 * @return <jk>true</jk> if this class is {@link Method}. 1202 */ 1203 public boolean isMethod() { 1204 return cc == METHOD; 1205 } 1206 1207 /** 1208 * Returns <jk>true</jk> if this class is an {@link Enum}. 1209 * 1210 * @return <jk>true</jk> if this class is an {@link Enum}. 1211 */ 1212 public boolean isEnum() { 1213 return cc == ENUM; 1214 } 1215 1216 /** 1217 * Returns <jk>true</jk> if this class is an array. 1218 * 1219 * @return <jk>true</jk> if this class is an array. 1220 */ 1221 public boolean isArray() { 1222 return cc == ARRAY; 1223 } 1224 1225 /** 1226 * Returns <jk>true</jk> if this class is a bean. 1227 * 1228 * @return <jk>true</jk> if this class is a bean. 1229 */ 1230 public boolean isBean() { 1231 return beanMeta != null; 1232 } 1233 1234 /** 1235 * Returns <jk>true</jk> if this class is {@link Object}. 1236 * 1237 * @return <jk>true</jk> if this class is {@link Object}. 1238 */ 1239 public boolean isObject() { 1240 return cc == OBJ; 1241 } 1242 1243 /** 1244 * Returns <jk>true</jk> if this class is not {@link Object}. 1245 * 1246 * @return <jk>true</jk> if this class is not {@link Object}. 1247 */ 1248 public boolean isNotObject() { 1249 return cc != OBJ; 1250 } 1251 1252 /** 1253 * Returns <jk>true</jk> if this class is a subclass of {@link Number}. 1254 * 1255 * @return <jk>true</jk> if this class is a subclass of {@link Number}. 1256 */ 1257 public boolean isNumber() { 1258 return cc == NUMBER || cc == DECIMAL; 1259 } 1260 1261 /** 1262 * Returns <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}. 1263 * 1264 * @return <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}. 1265 */ 1266 public boolean isDecimal() { 1267 return cc == DECIMAL; 1268 } 1269 1270 /** 1271 * Returns <jk>true</jk> if this class is either {@link Float} or <jk>float</jk>. 1272 * 1273 * @return <jk>true</jk> if this class is either {@link Float} or <jk>float</jk>. 1274 */ 1275 public boolean isFloat() { 1276 return innerClass == Float.class || innerClass == float.class; 1277 } 1278 1279 /** 1280 * Returns <jk>true</jk> if this class is either {@link Double} or <jk>double</jk>. 1281 * 1282 * @return <jk>true</jk> if this class is either {@link Double} or <jk>double</jk>. 1283 */ 1284 public boolean isDouble() { 1285 return innerClass == Double.class || innerClass == double.class; 1286 } 1287 1288 /** 1289 * Returns <jk>true</jk> if this class is either {@link Short} or <jk>short</jk>. 1290 * 1291 * @return <jk>true</jk> if this class is either {@link Short} or <jk>short</jk>. 1292 */ 1293 public boolean isShort() { 1294 return innerClass == Short.class || innerClass == short.class; 1295 } 1296 1297 /** 1298 * Returns <jk>true</jk> if this class is either {@link Integer} or <jk>int</jk>. 1299 * 1300 * @return <jk>true</jk> if this class is either {@link Integer} or <jk>int</jk>. 1301 */ 1302 public boolean isInteger() { 1303 return innerClass == Integer.class || innerClass == int.class; 1304 } 1305 1306 /** 1307 * Returns <jk>true</jk> if this class is either {@link Long} or <jk>long</jk>. 1308 * 1309 * @return <jk>true</jk> if this class is either {@link Long} or <jk>long</jk>. 1310 */ 1311 public boolean isLong() { 1312 return innerClass == Long.class || innerClass == long.class; 1313 } 1314 1315 /** 1316 * Returns <jk>true</jk> if this metadata represents the specified type. 1317 * 1318 * @param c The class to test against. 1319 * @return <jk>true</jk> if this metadata represents the specified type. 1320 */ 1321 public boolean isType(Class<?> c) { 1322 return isParentClass(c, innerClass); 1323 } 1324 1325 /** 1326 * Returns <jk>true</jk> if this class is a {@link Boolean}. 1327 * 1328 * @return <jk>true</jk> if this class is a {@link Boolean}. 1329 */ 1330 public boolean isBoolean() { 1331 return cc == BOOLEAN; 1332 } 1333 1334 /** 1335 * Returns <jk>true</jk> if this class is a subclass of {@link CharSequence}. 1336 * 1337 * @return <jk>true</jk> if this class is a subclass of {@link CharSequence}. 1338 */ 1339 public boolean isCharSequence() { 1340 return cc == STR || cc == CHARSEQ; 1341 } 1342 1343 /** 1344 * Returns <jk>true</jk> if this class is a {@link String}. 1345 * 1346 * @return <jk>true</jk> if this class is a {@link String}. 1347 */ 1348 public boolean isString() { 1349 return cc == STR; 1350 } 1351 1352 /** 1353 * Returns <jk>true</jk> if this class is a {@link Character}. 1354 * 1355 * @return <jk>true</jk> if this class is a {@link Character}. 1356 */ 1357 public boolean isChar() { 1358 return cc == CHAR; 1359 } 1360 1361 /** 1362 * Returns <jk>true</jk> if this class is a primitive. 1363 * 1364 * @return <jk>true</jk> if this class is a primitive. 1365 */ 1366 public boolean isPrimitive() { 1367 return innerClass.isPrimitive(); 1368 } 1369 1370 /** 1371 * Returns <jk>true</jk> if this class is a {@link Date} or {@link Calendar}. 1372 * 1373 * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar}. 1374 */ 1375 public boolean isDateOrCalendar() { 1376 return cc == DATE; 1377 } 1378 1379 /** 1380 * Returns <jk>true</jk> if this class is a {@link Date}. 1381 * 1382 * @return <jk>true</jk> if this class is a {@link Date}. 1383 */ 1384 public boolean isDate() { 1385 return cc == DATE && ClassUtils.isParentClass(Date.class, innerClass); 1386 } 1387 1388 /** 1389 * Returns <jk>true</jk> if this class is a {@link Calendar}. 1390 * 1391 * @return <jk>true</jk> if this class is a {@link Calendar}. 1392 */ 1393 public boolean isCalendar() { 1394 return cc == DATE && ClassUtils.isParentClass(Calendar.class, innerClass); 1395 } 1396 1397 /** 1398 * Returns <jk>true</jk> if this class is a {@link URI} or {@link URL}. 1399 * 1400 * @return <jk>true</jk> if this class is a {@link URI} or {@link URL}. 1401 */ 1402 public boolean isUri() { 1403 return cc == URI; 1404 } 1405 1406 /** 1407 * Returns <jk>true</jk> if this class is a {@link Reader}. 1408 * 1409 * @return <jk>true</jk> if this class is a {@link Reader}. 1410 */ 1411 public boolean isReader() { 1412 return cc == READER; 1413 } 1414 1415 /** 1416 * Returns <jk>true</jk> if this class is an {@link InputStream}. 1417 * 1418 * @return <jk>true</jk> if this class is an {@link InputStream}. 1419 */ 1420 public boolean isInputStream() { 1421 return cc == INPUTSTREAM; 1422 } 1423 1424 /** 1425 * Returns <jk>true</jk> if this class is {@link Void} or <jk>void</jk>. 1426 * 1427 * @return <jk>true</jk> if this class is {@link Void} or <jk>void</jk>. 1428 */ 1429 public boolean isVoid() { 1430 return cc == VOID; 1431 } 1432 1433 /** 1434 * Returns <jk>true</jk> if this metadata represents an array of argument types. 1435 * 1436 * @return <jk>true</jk> if this metadata represents an array of argument types. 1437 */ 1438 public boolean isArgs() { 1439 return cc == ARGS; 1440 } 1441 1442 /** 1443 * Returns the argument types of this meta. 1444 * 1445 * @return The argument types of this meta, or <jk>null</jk> if this isn't an array of argument types. 1446 */ 1447 public ClassMeta<?>[] getArgs() { 1448 return args; 1449 } 1450 1451 /** 1452 * Returns the argument metadata at the specified index if this is an args metadata object. 1453 * 1454 * @param index The argument index. 1455 * @return The The argument metadata. Never <jk>null</jk>. 1456 * @throws BeanRuntimeException If this metadata object is not a list of arguments, or the index is out of range. 1457 */ 1458 public ClassMeta<?> getArg(int index) { 1459 if (args != null && index >= 0 && index < args.length) 1460 return args[index]; 1461 throw new BeanRuntimeException("Invalid argument index specified: {0}. Only {1} arguments are defined.", index, args == null ? 0 : args.length); 1462 } 1463 1464 /** 1465 * Returns <jk>true</jk> if instance of this object can be <jk>null</jk>. 1466 * 1467 * <p> 1468 * Objects can be <jk>null</jk>, but primitives cannot, except for chars which can be represented by 1469 * <code>(<jk>char</jk>)0</code>. 1470 * 1471 * @return <jk>true</jk> if instance of this class can be null. 1472 */ 1473 public boolean isNullable() { 1474 if (innerClass.isPrimitive()) 1475 return cc == CHAR; 1476 return true; 1477 } 1478 1479 /** 1480 * Returns <jk>true</jk> if this class is abstract. 1481 * 1482 * @return <jk>true</jk> if this class is abstract. 1483 */ 1484 public boolean isAbstract() { 1485 return isAbstract; 1486 } 1487 1488 /** 1489 * Returns <jk>true</jk> if this class is an inner class. 1490 * 1491 * @return <jk>true</jk> if this class is an inner class. 1492 */ 1493 public boolean isMemberClass() { 1494 return isMemberClass; 1495 } 1496 1497 /** 1498 * All public methods on this class including static methods. 1499 * 1500 * <p> 1501 * Keys are method signatures. 1502 * 1503 * @return The public methods on this class. 1504 */ 1505 public Map<String,Method> getPublicMethods() { 1506 return publicMethods; 1507 } 1508 1509 /** 1510 * Returns the {@link PojoSwap} associated with this class that's the best match for the specified session. 1511 * 1512 * @param session 1513 * The current bean session. 1514 * <br>If multiple swaps are associated with a class, only the first one with a matching media type will 1515 * be returned. 1516 * @return 1517 * The {@link PojoSwap} associated with this class, or <jk>null</jk> if there are no POJO swaps associated with 1518 * this class. 1519 */ 1520 public PojoSwap<T,?> getPojoSwap(BeanSession session) { 1521 if (pojoSwaps != null) { 1522 int matchQuant = 0, matchIndex = -1; 1523 1524 for (int i = 0; i < pojoSwaps.length; i++) { 1525 int q = pojoSwaps[i].match(session); 1526 if (q > matchQuant) { 1527 matchQuant = q; 1528 matchIndex = i; 1529 } 1530 } 1531 1532 if (matchIndex > -1) 1533 return pojoSwaps[matchIndex]; 1534 } 1535 return null; 1536 } 1537 1538 /** 1539 * Returns the builder swap associated with this class. 1540 * 1541 * @param session The current bean session. 1542 * @return The builder swap associated with this class, or <jk>null</jk> if it doesn't exist. 1543 */ 1544 public BuilderSwap<T,?> getBuilderSwap(BeanSession session) { 1545 return builderSwap; 1546 } 1547 1548 /** 1549 * Returns the {@link BeanMeta} associated with this class. 1550 * 1551 * @return 1552 * The {@link BeanMeta} associated with this class, or <jk>null</jk> if there is no bean meta associated with 1553 * this class. 1554 */ 1555 public BeanMeta<T> getBeanMeta() { 1556 return beanMeta; 1557 } 1558 1559 /** 1560 * Returns the no-arg constructor for this class. 1561 * 1562 * @return The no-arg constructor for this class, or <jk>null</jk> if it does not exist. 1563 */ 1564 public Constructor<? extends T> getConstructor() { 1565 return noArgConstructor; 1566 } 1567 1568 /** 1569 * Returns the language-specified extended metadata on this class. 1570 * 1571 * @param c The name of the metadata class to create. 1572 * @return Extended metadata on this class. Never <jk>null</jk>. 1573 */ 1574 public <M extends ClassMetaExtended> M getExtendedMeta(Class<M> c) { 1575 return extMeta.get(c, this); 1576 } 1577 1578 /** 1579 * Returns the interface proxy invocation handler for this class. 1580 * 1581 * @return The interface proxy invocation handler, or <jk>null</jk> if it does not exist. 1582 */ 1583 public InvocationHandler getProxyInvocationHandler() { 1584 return invocationHandler; 1585 } 1586 1587 /** 1588 * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler. 1589 * 1590 * @return <jk>true</jk> if a new instance of this class can be constructed. 1591 */ 1592 public boolean canCreateNewInstance() { 1593 if (isMemberClass) 1594 return false; 1595 if (noArgConstructor != null) 1596 return true; 1597 if (getProxyInvocationHandler() != null) 1598 return true; 1599 if (isArray() && elementType.canCreateNewInstance()) 1600 return true; 1601 return false; 1602 } 1603 1604 /** 1605 * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler. 1606 * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match the class type of 1607 * the defining class. 1608 * 1609 * @param outer 1610 * The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes. 1611 * @return 1612 * <jk>true</jk> if a new instance of this class can be created within the context of the specified outer object. 1613 */ 1614 public boolean canCreateNewInstance(Object outer) { 1615 if (isMemberClass) 1616 return outer != null && noArgConstructor != null && hasArgs(noArgConstructor, outer.getClass()); 1617 return canCreateNewInstance(); 1618 } 1619 1620 /** 1621 * Returns <jk>true</jk> if this class can be instantiated as a bean. 1622 * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match the class type of 1623 * the defining class. 1624 * 1625 * @param outer 1626 * The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes. 1627 * @return 1628 * <jk>true</jk> if a new instance of this bean can be created within the context of the specified outer object. 1629 */ 1630 public boolean canCreateNewBean(Object outer) { 1631 if (beanMeta == null) 1632 return false; 1633 if (beanMeta.constructor == null) 1634 return false; 1635 if (isMemberClass) 1636 return outer != null && hasArgs(beanMeta.constructor, outer.getClass()); 1637 return true; 1638 } 1639 1640 /** 1641 * Returns <jk>true</jk> if this class can call the {@link #newInstanceFromString(Object, String)} method. 1642 * 1643 * @param outer 1644 * The outer class object for non-static member classes. 1645 * Can be <jk>null</jk> for non-member or static classes. 1646 * @return <jk>true</jk> if this class has a no-arg constructor or invocation handler. 1647 */ 1648 public boolean canCreateNewInstanceFromString(Object outer) { 1649 if (fromStringMethod != null) 1650 return true; 1651 if (stringConstructor != null) { 1652 if (isMemberClass) 1653 return outer != null && hasArgs(stringConstructor, outer.getClass(), String.class); 1654 return true; 1655 } 1656 return false; 1657 } 1658 1659 /** 1660 * Returns <jk>true</jk> if this class can call the {@link #newInstanceFromString(Object, String)} method. 1661 * 1662 * @param outer 1663 * The outer class object for non-static member classes. 1664 * Can be <jk>null</jk> for non-member or static classes. 1665 * @return <jk>true</jk> if this class has a no-arg constructor or invocation handler. 1666 */ 1667 public boolean canCreateNewInstanceFromNumber(Object outer) { 1668 if (numberConstructor != null) { 1669 if (isMemberClass) 1670 return outer != null && hasArgs(numberConstructor, outer.getClass()); 1671 return true; 1672 } 1673 return false; 1674 } 1675 1676 /** 1677 * Returns the class type of the parameter of the numeric constructor. 1678 * 1679 * @return The class type of the numeric constructor, or <jk>null</jk> if no such constructor exists. 1680 */ 1681 @SuppressWarnings("unchecked") 1682 public Class<? extends Number> getNewInstanceFromNumberClass() { 1683 return (Class<? extends Number>) numberConstructorType; 1684 } 1685 1686 /** 1687 * Returns the method or field annotated with {@link NameProperty @NameProperty}. 1688 * 1689 * @return 1690 * The method or field annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not 1691 * exist. 1692 */ 1693 public Setter getNameProperty() { 1694 return namePropertyMethod; 1695 } 1696 1697 /** 1698 * Returns the method or field annotated with {@link ParentProperty @ParentProperty}. 1699 * 1700 * @return 1701 * The method or field annotated with {@link ParentProperty @ParentProperty} or <jk>null</jk> if method does not 1702 * exist. 1703 */ 1704 public Setter getParentProperty() { 1705 return parentPropertyMethod; 1706 } 1707 1708 /** 1709 * Returns the reason why this class is not a bean, or <jk>null</jk> if it is a bean. 1710 * 1711 * @return The reason why this class is not a bean, or <jk>null</jk> if it is a bean. 1712 */ 1713 public synchronized String getNotABeanReason() { 1714 return notABeanReason; 1715 } 1716 1717 /** 1718 * Returns any exception that was throw in the <code>init()</code> method. 1719 * 1720 * @return The cached exception. 1721 */ 1722 public Throwable getInitException() { 1723 return initException; 1724 } 1725 1726 /** 1727 * Returns the {@link BeanContext} that created this object. 1728 * 1729 * @return The bean context. 1730 */ 1731 public BeanContext getBeanContext() { 1732 return beanContext; 1733 } 1734 1735 /** 1736 * Returns the default value for primitives such as <jk>int</jk> or <jk>Integer</jk>. 1737 * 1738 * @return The default value, or <jk>null</jk> if this class type is not a primitive. 1739 */ 1740 @SuppressWarnings("unchecked") 1741 public T getPrimitiveDefault() { 1742 return (T)primitiveDefault; 1743 } 1744 1745 /** 1746 * Converts the specified object to a string. 1747 * 1748 * @param t The object to convert. 1749 * @return The object converted to a string, or <jk>null</jk> if the object was null. 1750 */ 1751 public String toString(Object t) { 1752 if (t == null) 1753 return null; 1754 if (isEnum() && beanContext.isUseEnumNames()) 1755 return ((Enum<?>)t).name(); 1756 return t.toString(); 1757 } 1758 1759 /** 1760 * Create a new instance of the main class of this declared type from a <code>String</code> input. 1761 * 1762 * <p> 1763 * In order to use this method, the class must have one of the following methods: 1764 * <ul> 1765 * <li><code><jk>public static</jk> T valueOf(String in);</code> 1766 * <li><code><jk>public static</jk> T fromString(String in);</code> 1767 * <li><code><jk>public</jk> T(String in);</code> 1768 * </ul> 1769 * 1770 * @param outer 1771 * The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes. 1772 * @param arg The input argument value. 1773 * @return A new instance of the object, or <jk>null</jk> if there is no string constructor on the object. 1774 * @throws IllegalAccessException 1775 * If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is 1776 * inaccessible. 1777 * @throws IllegalArgumentException If the parameter type on the method was invalid. 1778 * @throws InstantiationException 1779 * If the class that declares the underlying constructor represents an abstract class, or does not have one of 1780 * the methods described above. 1781 * @throws InvocationTargetException If the underlying constructor throws an exception. 1782 */ 1783 @SuppressWarnings({ "unchecked", "rawtypes" }) 1784 public T newInstanceFromString(Object outer, String arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { 1785 1786 if (isEnum() && beanContext.isUseEnumNames()) 1787 return (T)Enum.valueOf((Class<? extends Enum>)this.innerClass, arg); 1788 1789 Method m = fromStringMethod; 1790 if (m != null) 1791 return (T)m.invoke(null, arg); 1792 Constructor<T> c = stringConstructor; 1793 if (c != null) { 1794 if (isMemberClass) 1795 return c.newInstance(outer, arg); 1796 return c.newInstance(arg); 1797 } 1798 throw new InstantiationError("No string constructor or valueOf(String) method found for class '"+getInnerClass().getName()+"'"); 1799 } 1800 1801 /** 1802 * Create a new instance of the main class of this declared type from a <code>Number</code> input. 1803 * 1804 * <p> 1805 * In order to use this method, the class must have one of the following methods: 1806 * <ul> 1807 * <li><code><jk>public</jk> T(Number in);</code> 1808 * </ul> 1809 * 1810 * @param session The current bean session. 1811 * @param outer 1812 * The outer class object for non-static member classes. 1813 * Can be <jk>null</jk> for non-member or static classes. 1814 * @param arg The input argument value. 1815 * @return A new instance of the object, or <jk>null</jk> if there is no numeric constructor on the object. 1816 * @throws IllegalAccessException 1817 * If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is 1818 * inaccessible. 1819 * @throws IllegalArgumentException If the parameter type on the method was invalid. 1820 * @throws InstantiationException 1821 * If the class that declares the underlying constructor represents an abstract class, or does not have one of 1822 * the methods described above. 1823 * @throws InvocationTargetException If the underlying constructor throws an exception. 1824 */ 1825 public T newInstanceFromNumber(BeanSession session, Object outer, Number arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { 1826 Constructor<T> c = numberConstructor; 1827 if (c != null) { 1828 Object arg2 = session.convertToType(arg, numberConstructor.getParameterTypes()[0]); 1829 if (isMemberClass) 1830 return c.newInstance(outer, arg2); 1831 return c.newInstance(arg2); 1832 } 1833 throw new InstantiationError("No string constructor or valueOf(Number) method found for class '"+getInnerClass().getName()+"'"); 1834 } 1835 1836 /** 1837 * Create a new instance of the main class of this declared type. 1838 * 1839 * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object. 1840 * @throws IllegalAccessException 1841 * If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is 1842 * inaccessible. 1843 * @throws IllegalArgumentException 1844 * If one of the following occurs: 1845 * <ul class='spaced-list'> 1846 * <li> 1847 * The number of actual and formal parameters differ. 1848 * <li> 1849 * An unwrapping conversion for primitive arguments fails. 1850 * <li> 1851 * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation 1852 * conversion. 1853 * <li> 1854 * The constructor pertains to an enum type. 1855 * </ul> 1856 * @throws InstantiationException If the class that declares the underlying constructor represents an abstract class. 1857 * @throws InvocationTargetException If the underlying constructor throws an exception. 1858 */ 1859 @SuppressWarnings("unchecked") 1860 public T newInstance() throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { 1861 if (isArray()) 1862 return (T)Array.newInstance(getInnerClass().getComponentType(), 0); 1863 Constructor<? extends T> c = getConstructor(); 1864 if (c != null) 1865 return c.newInstance((Object[])null); 1866 InvocationHandler h = getProxyInvocationHandler(); 1867 if (h != null) 1868 return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { getInnerClass(), java.io.Serializable.class }, h); 1869 if (isArray()) 1870 return (T)Array.newInstance(this.elementType.innerClass,0); 1871 return null; 1872 } 1873 1874 /** 1875 * Same as {@link #newInstance()} except for instantiating non-static member classes. 1876 * 1877 * @param outer 1878 * The instance of the owning object of the member class instance. 1879 * Can be <jk>null</jk> if instantiating a non-member or static class. 1880 * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object. 1881 * @throws IllegalAccessException 1882 * If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is 1883 * inaccessible. 1884 * @throws IllegalArgumentException 1885 * If one of the following occurs: 1886 * <ul class='spaced-list'> 1887 * <li> 1888 * The number of actual and formal parameters differ. 1889 * <li> 1890 * An unwrapping conversion for primitive arguments fails. 1891 * <li> 1892 * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation 1893 * conversion. 1894 * <li> 1895 * The constructor pertains to an enum type. 1896 * </ul> 1897 * @throws InstantiationException If the class that declares the underlying constructor represents an abstract class. 1898 * @throws InvocationTargetException If the underlying constructor throws an exception. 1899 */ 1900 public T newInstance(Object outer) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { 1901 if (isMemberClass) 1902 return noArgConstructor.newInstance(outer); 1903 return newInstance(); 1904 } 1905 1906 /** 1907 * Checks to see if the specified class type is the same as this one. 1908 * 1909 * @param t The specified class type. 1910 * @return <jk>true</jk> if the specified class type is the same as the class for this type. 1911 */ 1912 @Override /* Object */ 1913 public boolean equals(Object t) { 1914 if (t == null || ! (t instanceof ClassMeta)) 1915 return false; 1916 ClassMeta<?> t2 = (ClassMeta<?>)t; 1917 return t2.getInnerClass() == this.getInnerClass(); 1918 } 1919 1920 /** 1921 * Similar to {@link #equals(Object)} except primitive and Object types that are similar are considered the same. 1922 * (e.g. <jk>boolean</jk> == <code>Boolean</code>). 1923 * 1924 * @param cm The class meta to compare to. 1925 * @return <jk>true</jk> if the specified class-meta is equivalent to this one. 1926 */ 1927 public boolean same(ClassMeta<?> cm) { 1928 if (equals(cm)) 1929 return true; 1930 return (isPrimitive() && cc == cm.cc); 1931 } 1932 1933 @Override /* Object */ 1934 public String toString() { 1935 return toString(false); 1936 } 1937 1938 /** 1939 * Same as {@link #toString()} except use simple class names. 1940 * 1941 * @param simple Print simple class names only (no package). 1942 * @return A new string. 1943 */ 1944 public String toString(boolean simple) { 1945 return toString(new StringBuilder(), simple).toString(); 1946 } 1947 1948 /** 1949 * Appends this object as a readable string to the specified string builder. 1950 * 1951 * @param sb The string builder to append this object to. 1952 * @param simple Print simple class names only (no package). 1953 * @return The same string builder passed in (for method chaining). 1954 */ 1955 protected StringBuilder toString(StringBuilder sb, boolean simple) { 1956 String n = innerClass.getName(); 1957 if (simple) { 1958 int i = n.lastIndexOf('.'); 1959 n = n.substring(i == -1 ? 0 : i+1).replace('$', '.'); 1960 } 1961 if (cc == ARRAY) 1962 return elementType.toString(sb, simple).append('[').append(']'); 1963 if (cc == MAP) 1964 return sb.append(n).append(keyType.isObject() && valueType.isObject() ? "" : "<"+keyType.toString(simple)+","+valueType.toString(simple)+">"); 1965 if (cc == BEANMAP) 1966 return sb.append(BeanMap.class.getName()).append('<').append(n).append('>'); 1967 if (cc == COLLECTION) 1968 return sb.append(n).append(elementType.isObject() ? "" : "<"+elementType.toString(simple)+">"); 1969 return sb.append(n); 1970 } 1971 1972 /** 1973 * Returns <jk>true</jk> if the specified object is an instance of this class. 1974 * 1975 * <p> 1976 * This is a simple comparison on the base class itself and not on any generic parameters. 1977 * 1978 * @param o The object to check. 1979 * @return <jk>true</jk> if the specified object is an instance of this class. 1980 */ 1981 public boolean isInstance(Object o) { 1982 if (o != null) 1983 return isParentClass(this.innerClass, o.getClass()) || (isPrimitive() && getPrimitiveWrapper(this.innerClass) == o.getClass()); 1984 return false; 1985 } 1986 1987 /** 1988 * Returns a readable name for this class (e.g. <js>"java.lang.String"</js>, <js>"boolean[]"</js>). 1989 * 1990 * @return The readable name for this class. 1991 */ 1992 public String getReadableName() { 1993 return getReadableClassName(this.innerClass); 1994 } 1995 1996 /** 1997 * Shortcut for calling {@link Class#getName()} on the inner class of this metadata. 1998 * 1999 * @return The name of the inner class. 2000 */ 2001 public String getName() { 2002 return innerClass.getName(); 2003 } 2004 2005 /** 2006 * Shortcut for calling {@link Class#getSimpleName()} on the inner class of this metadata. 2007 * 2008 * @return The simple name of the inner class. 2009 */ 2010 public String getSimpleName() { 2011 return innerClass.getSimpleName(); 2012 } 2013 2014 private static class LocaleAsString { 2015 private static Method forLanguageTagMethod; 2016 static { 2017 try { 2018 forLanguageTagMethod = Locale.class.getMethod("forLanguageTag", String.class); 2019 } catch (NoSuchMethodException e) {} 2020 } 2021 2022 @SuppressWarnings("unused") 2023 public static final Locale fromString(String localeString) { 2024 if (forLanguageTagMethod != null) { 2025 if (localeString.indexOf('_') != -1) 2026 localeString = localeString.replace('_', '-'); 2027 try { 2028 return (Locale)forLanguageTagMethod.invoke(null, localeString); 2029 } catch (Exception e) { 2030 throw new BeanRuntimeException(e); 2031 } 2032 } 2033 String[] v = localeString.toString().split("[\\-\\_]"); 2034 if (v.length == 1) 2035 return new Locale(v[0]); 2036 else if (v.length == 2) 2037 return new Locale(v[0], v[1]); 2038 else if (v.length == 3) 2039 return new Locale(v[0], v[1], v[2]); 2040 throw new BeanRuntimeException("Could not convert string ''{0}'' to a Locale.", localeString); 2041 } 2042 } 2043 2044 @Override /* Object */ 2045 public int hashCode() { 2046 return super.hashCode(); 2047 } 2048 2049 /** 2050 * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader. 2051 * 2052 * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader. 2053 */ 2054 public boolean hasReaderTransform() { 2055 return hasTransformFrom(Reader.class); 2056 } 2057 2058 /** 2059 * Returns the transform for this class for creating instances from a Reader. 2060 * 2061 * @return The transform, or <jk>null</jk> if no such transform exists. 2062 */ 2063 public Transform<Reader,T> getReaderTransform() { 2064 return getFromTransform(Reader.class); 2065 } 2066 2067 /** 2068 * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream. 2069 * 2070 * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream. 2071 */ 2072 public boolean hasInputStreamTransform() { 2073 return hasTransformFrom(InputStream.class); 2074 } 2075 2076 /** 2077 * Returns the transform for this class for creating instances from an InputStream. 2078 * 2079 * @return The transform, or <jk>null</jk> if no such transform exists. 2080 */ 2081 public Transform<InputStream,T> getInputStreamTransform() { 2082 return getFromTransform(InputStream.class); 2083 } 2084 2085 /** 2086 * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a String. 2087 * 2088 * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a String. 2089 */ 2090 public boolean hasStringTransform() { 2091 return stringTransform != null; 2092 } 2093 2094 /** 2095 * Returns the transform for this class for creating instances from a String. 2096 * 2097 * @return The transform, or <jk>null</jk> if no such transform exists. 2098 */ 2099 public Transform<String,T> getStringTransform() { 2100 return stringTransform; 2101 } 2102 2103 /** 2104 * Returns <jk>true</jk> if this class can be instantiated from the specified type. 2105 * 2106 * @param c The class type to convert from. 2107 * @return <jk>true</jk> if this class can be instantiated from the specified type. 2108 */ 2109 public boolean hasTransformFrom(Class<?> c) { 2110 return getFromTransform(c) != null; 2111 } 2112 2113 /** 2114 * Returns <jk>true</jk> if this class can be instantiated from the specified type. 2115 * 2116 * @param c The class type to convert from. 2117 * @return <jk>true</jk> if this class can be instantiated from the specified type. 2118 */ 2119 public boolean hasTransformFrom(ClassMeta<?> c) { 2120 return getFromTransform(c.getInnerClass()) != null; 2121 } 2122 2123 /** 2124 * Returns <jk>true</jk> if this class can be transformed to the specified type. 2125 * 2126 * @param c The class type to convert from. 2127 * @return <jk>true</jk> if this class can be transformed to the specified type. 2128 */ 2129 public boolean hasTransformTo(Class<?> c) { 2130 return getToTransform(c) != null; 2131 } 2132 2133 /** 2134 * Returns <jk>true</jk> if this class can be transformed to the specified type. 2135 * 2136 * @param c The class type to convert from. 2137 * @return <jk>true</jk> if this class can be transformed to the specified type. 2138 */ 2139 public boolean hasTransformTo(ClassMeta<?> c) { 2140 return getToTransform(c.getInnerClass()) != null; 2141 } 2142 2143 /** 2144 * Transforms the specified object into an instance of this class. 2145 * 2146 * @param o The object to transform. 2147 * @return The transformed object. 2148 */ 2149 @SuppressWarnings({"unchecked","rawtypes"}) 2150 public T transformFrom(Object o) { 2151 Transform t = getFromTransform(o.getClass()); 2152 return (T)(t == null ? null : t.transform(o)); 2153 } 2154 2155 /** 2156 * Transforms the specified object into an instance of this class. 2157 * 2158 * @param o The object to transform. 2159 * @param c The class 2160 * @return The transformed object. 2161 */ 2162 @SuppressWarnings({"unchecked","rawtypes"}) 2163 public <O> O transformTo(Object o, Class<O> c) { 2164 Transform t = getToTransform(c); 2165 return (O)(t == null ? null : t.transform(o)); 2166 } 2167 2168 /** 2169 * Transforms the specified object into an instance of this class. 2170 * 2171 * @param o The object to transform. 2172 * @param c The class 2173 * @return The transformed object. 2174 */ 2175 public <O> O transformTo(Object o, ClassMeta<O> c) { 2176 return transformTo(o, c.getInnerClass()); 2177 } 2178 2179 /** 2180 * Returns the transform for this class for creating instances from other object types. 2181 * 2182 * @param c The transform-from class. 2183 * @return The transform, or <jk>null</jk> if no such transform exists. 2184 */ 2185 @SuppressWarnings({ "rawtypes", "unchecked" }) 2186 public <I> Transform<I,T> getFromTransform(Class<I> c) { 2187 Transform t = fromTransforms.get(c); 2188 if (t == TransformCache.NULL) 2189 return null; 2190 if (t == null) { 2191 t = TransformCache.get(c, innerClass); 2192 if (t == null) 2193 t = TransformCache.NULL; 2194 fromTransforms.put(c, t); 2195 } 2196 return t == TransformCache.NULL ? null : t; 2197 } 2198 2199 /** 2200 * Returns the transform for this class for creating instances from other object types. 2201 * 2202 * @param c The transform-from class. 2203 * @return The transform, or <jk>null</jk> if no such transform exists. 2204 */ 2205 @SuppressWarnings({ "rawtypes", "unchecked" }) 2206 public <O> Transform<T,O> getToTransform(Class<O> c) { 2207 Transform t = toTransforms.get(c); 2208 if (t == TransformCache.NULL) 2209 return null; 2210 if (t == null) { 2211 t = TransformCache.get(innerClass, c); 2212 if (t == null) 2213 t = TransformCache.NULL; 2214 toTransforms.put(c, t); 2215 } 2216 return t == TransformCache.NULL ? null : t; 2217 } 2218 2219 /** 2220 * Shortcut for calling <code>getInnerClass().getAnnotation(a) != <jk>null</jk></code>. 2221 * 2222 * @param a The annotation to check for. 2223 * @return <jk>true</jk> if the inner class has the annotation. 2224 */ 2225 public boolean hasAnnotation(Class<? extends Annotation> a) { 2226 return getAnnotation(a) != null; 2227 } 2228 2229 /** 2230 * Shortcut for calling <code>getInnerClass().getAnnotation(a)</code>. 2231 * 2232 * @param a The annotation to retrieve. 2233 * @return The specified annotation, or <jk>null</jk> if the class does not have the specified annotation. 2234 */ 2235 public <A extends Annotation> A getAnnotation(Class<A> a) { 2236 return this.innerClass.getAnnotation(a); 2237 } 2238}