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