Improvements to ClassMeta/BeanContext (2). Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/062d7ddc Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/062d7ddc Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/062d7ddc
Branch: refs/heads/master Commit: 062d7ddc65b462128c43b9673c20104dcfd58e80 Parents: c8fa4ed Author: JamesBognar <[email protected]> Authored: Sat Feb 4 17:44:09 2017 -0500 Committer: JamesBognar <[email protected]> Committed: Sat Feb 4 17:44:09 2017 -0500 ---------------------------------------------------------------------- .../java/org/apache/juneau/BeanContext.java | 48 +++- .../org/apache/juneau/BeanPropertyMeta.java | 8 +- .../main/java/org/apache/juneau/ClassMeta.java | 237 +++++++++++++------ .../org/apache/juneau/transform/PojoSwap.java | 6 + 4 files changed, 207 insertions(+), 92 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/BeanContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java index 05006f3..1cb9180 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java @@ -1155,8 +1155,8 @@ public class BeanContext extends Context { if (! cmCacheCache.containsKey(hashCode)) { ConcurrentHashMap<Class,ClassMeta> cm = new ConcurrentHashMap<Class,ClassMeta>(); - cm.put(String.class, new ClassMeta(String.class, this)); - cm.put(Object.class, new ClassMeta(Object.class, this)); + cm.put(String.class, new ClassMeta(String.class, this, null, null, findPojoSwap(String.class), findChildPojoSwaps(String.class))); + cm.put(Object.class, new ClassMeta(Object.class, this, null, null, findPojoSwap(Object.class), findChildPojoSwaps(Object.class))); cmCacheCache.putIfAbsent(hashCode, cm); } this.cmCache = cmCacheCache.get(hashCode); @@ -1385,7 +1385,7 @@ public class BeanContext extends Context { // Note that if it has a pojo swap, we still want to cache it so that // we can cache something like byte[] with ByteArrayBase64Swap. if (c.isArray() && findPojoSwap(c) == null) - return new ClassMeta(c, this); + return new ClassMeta(c, this, findImplClass(c), findBeanFilter(c), findPojoSwap(c), findChildPojoSwaps(c)); // This can happen if we have transforms defined against String or Object. if (cmCache == null) @@ -1407,7 +1407,7 @@ public class BeanContext extends Context { if (pcm.innerClass == c) return pcm; - cm = new ClassMeta<T>(c, this, true); + cm = new ClassMeta<T>(c, this, findImplClass(c), findBeanFilter(c), findPojoSwap(c), findChildPojoSwaps(c), true); pendingClassMetas.addLast(cm); try { cm.init(); @@ -1751,7 +1751,7 @@ public class BeanContext extends Context { * @param c The class associated with the swap. * @return The swap associated with the class, or null if there is no association. */ - protected final <T> PojoSwap findPojoSwap(Class<T> c) { + private final <T> PojoSwap findPojoSwap(Class<T> c) { // Note: On first if (c != null) for (PojoSwap f : pojoSwaps) @@ -1765,12 +1765,18 @@ public class BeanContext extends Context { * @param c The class to check. * @return <jk>true</jk> if the specified class or one of its subclasses has a {@link PojoSwap} associated with it. */ - protected final boolean hasChildPojoSwaps(Class<?> c) { - if (c != null) - for (PojoSwap f : pojoSwaps) - if (isParentClass(c, f.getNormalClass())) - return true; - return false; + private final PojoSwap[] findChildPojoSwaps(Class<?> c) { + if (c == null || pojoSwaps.length == 0) + return null; + List<PojoSwap> l = null; + for (PojoSwap f : pojoSwaps) { + if (isParentClass(c, f.getNormalClass())) { + if (l == null) + l = new ArrayList<PojoSwap>(); + l.add(f); + } + } + return l == null ? null : l.toArray(new PojoSwap[l.size()]); } /** @@ -1781,7 +1787,7 @@ public class BeanContext extends Context { * @param c The class associated with the bean filter. * @return The bean filter associated with the class, or null if there is no association. */ - protected final <T> BeanFilter findBeanFilter(Class<T> c) { + private final <T> BeanFilter findBeanFilter(Class<T> c) { if (c != null) for (BeanFilter f : beanFilters) if (isParentClass(f.getBeanClass(), c)) @@ -1833,6 +1839,24 @@ public class BeanContext extends Context { return null; } + private final <T> Class<? extends T> findImplClass(Class<T> c) { + if (implClasses.isEmpty()) + return null; + Class cc = c; + while (cc != null) { + Class implClass = implClasses.get(cc); + if (implClass != null) + return implClass; + for (Class ic : cc.getInterfaces()) { + implClass = implClasses.get(ic); + if (implClass != null) + return implClass; + } + cc = cc.getSuperclass(); + } + return null; + } + /** * Returns a reusable {@link ClassMeta} representation for the class <code>Object</code>. * <p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java index ce7fdb5..a9e52f0 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java @@ -848,9 +848,7 @@ public class BeanPropertyMeta { return null; // Otherwise, look it up via bean context. if (rawTypeMeta.hasChildPojoSwaps()) { - Class c = o.getClass(); - ClassMeta<?> cm = rawTypeMeta.innerClass == c ? rawTypeMeta : beanContext.getClassMeta(c); - PojoSwap f = cm.getPojoSwap(); + PojoSwap f = rawTypeMeta.getChildPojoSwapForSwap(o.getClass()); if (f != null) return f.swap(session, o); } @@ -863,9 +861,7 @@ public class BeanPropertyMeta { if (o == null) return null; if (rawTypeMeta.hasChildPojoSwaps()) { - Class c = o.getClass(); - ClassMeta<?> cm = rawTypeMeta.innerClass == c ? rawTypeMeta : beanContext.getClassMeta(c); - PojoSwap f = cm.getPojoSwap(); + PojoSwap f = rawTypeMeta.getChildPojoSwapForUnswap(o.getClass()); if (f != null) return f.unswap(session, o, rawTypeMeta); } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java index e98f595..5e079c4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java @@ -21,6 +21,7 @@ import java.lang.reflect.Proxy; import java.net.*; import java.net.URI; import java.util.*; +import java.util.concurrent.*; import org.apache.juneau.annotation.*; import org.apache.juneau.internal.*; @@ -57,9 +58,10 @@ public final class ClassMeta<T> implements Type { } final Class<T> innerClass; // The class being wrapped. + final Class<? extends T> implClass; // The implementation class to use if this is an interface. final ClassCategory cc; // The class category. final Method fromStringMethod; // The static valueOf(String) or fromString(String) or forString(String) method (if it has one). - Constructor<? extends T> + final Constructor<? extends T> noArgConstructor; // The no-arg constructor for this class (if it has one). final Constructor<T> stringConstructor, // The X(String) constructor (if it has one). @@ -82,7 +84,10 @@ public final class ClassMeta<T> implements Type { final Map<String,Method> remoteableMethods, // Methods annotated with @Remoteable. Contains all public methods if class is annotated with @Remotable. publicMethods; // All public methods, including static methods. - + final PojoSwap<?,?>[] childPojoSwaps; // Any PojoSwaps where the normal type is a subclass of this class. + final ConcurrentHashMap<Class<?>,PojoSwap<?,?>> + childSwapMap, // Maps normal subclasses to PojoSwaps. + childUnswapMap; // Maps swap subclasses to PojoSwaps. final BeanContext beanContext; // The bean context that created this object. ClassMeta<?> @@ -99,7 +104,6 @@ public final class ClassMeta<T> implements Type { private MetadataMap extMeta = new MetadataMap(); // Extended metadata private Throwable initException; // Any exceptions thrown in the init() method. - private boolean hasChildPojoSwaps; // True if this class or any subclass of this class has a PojoSwap associated with it. private static final Boolean BOOLEAN_DEFAULT = false; private static final Character CHARACTER_DEFAULT = (char)0; @@ -113,8 +117,8 @@ public final class ClassMeta<T> implements Type { /** * Shortcut for calling <code>ClassMeta(innerClass, beanContext, <jk>false</jk>)</code>. */ - ClassMeta(Class<T> innerClass, BeanContext beanContext) { - this(innerClass, beanContext, false); + ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] childPojoSwaps) { + this(innerClass, beanContext, implClass, beanFilter, pojoSwap, childPojoSwaps, false); } /** @@ -122,13 +126,26 @@ public final class ClassMeta<T> implements Type { * * @param innerClass The class being wrapped. * @param beanContext The bean context that created this object. + * @param implClass For interfaces and abstract classes, this represents the "real" class to instantiate. + * Can be <jk>null</jk>. + * @param beanFilter The {@link BeanFilter} programmatically associated with this class. + * Can be <jk>null</jk>. + * @param pojoSwap The {@link PojoSwap} programmatically associated with this class. + * Can be <jk>null</jk>. + * @param childPojoSwap The child {@link PojoSwap PojoSwaps} programmatically associated with this class. + * These are the <code>PojoSwaps</code> that have normal classes that are subclasses of this class. + * Can be <jk>null</jk>. * @param delayedInit Don't call init() in constructor. * Used for delayed initialization when the possibility of class reference loops exist. */ @SuppressWarnings({ "rawtypes", "unchecked" }) - ClassMeta(Class<T> innerClass, BeanContext beanContext, boolean delayedInit) { + ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] childPojoSwaps, boolean delayedInit) { this.innerClass = innerClass; this.beanContext = beanContext; + this.implClass = implClass; + this.childPojoSwaps = childPojoSwaps; + this.childSwapMap = childPojoSwaps == null ? null : new ConcurrentHashMap<Class<?>,PojoSwap<?,?>>(); + this.childUnswapMap = childPojoSwaps == null ? null : new ConcurrentHashMap<Class<?>,PojoSwap<?,?>>(); Class<T> c = innerClass; ClassCategory _cc = ClassCategory.OTHER; @@ -351,6 +368,41 @@ public final class ClassMeta<T> implements Type { } } + if (innerClass != Object.class) { + _noArgConstructor = (Constructor<T>)findNoArgConstructor(implClass == null ? innerClass : implClass, Visibility.PUBLIC); + } + + if (beanFilter == null) + beanFilter = findBeanFilter(); + + PojoSwap ps = null; + if (_swapMethod != null) { + ps = new PojoSwap<T,Object>(c, _swapMethod.getReturnType()) { + @Override + public Object swap(BeanSession session, Object o) throws SerializeException { + try { + return swapMethod.invoke(o, session); + } catch (Exception e) { + throw new SerializeException(e); + } + } + @Override + public T unswap(BeanSession session, Object f, ClassMeta<?> hint) throws ParseException { + try { + if (swapConstructor != null) + return swapConstructor.newInstance(f); + return super.unswap(session, f, hint); + } catch (Exception e) { + throw new ParseException(e); + } + } + }; + } + if (ps == null) + ps = findPojoSwap(); + if (ps == null) + ps = pojoSwap; + this.cc = _cc; this.isDelegate = _isDelegate; this.fromStringMethod = _fromStringMethod; @@ -368,6 +420,8 @@ public final class ClassMeta<T> implements Type { this.primitiveDefault = _primitiveDefault; this.publicMethods = _publicMethods; this.remoteableMethods = _remoteableMethods; + this.beanFilter = beanFilter; + this.pojoSwap = ps; if (! delayedInit) init(); @@ -379,6 +433,10 @@ public final class ClassMeta<T> implements Type { */ ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> valueType, ClassMeta<?> elementType) { this.innerClass = mainType.innerClass; + this.implClass = mainType.implClass; + this.childPojoSwaps = mainType.childPojoSwaps; + this.childSwapMap = mainType.childSwapMap; + this.childUnswapMap = mainType.childUnswapMap; this.cc = mainType.cc; this.fromStringMethod = mainType.fromStringMethod; this.noArgConstructor = mainType.noArgConstructor; @@ -412,23 +470,12 @@ public final class ClassMeta<T> implements Type { this.beanFilter = mainType.beanFilter; this.extMeta = mainType.extMeta; this.initException = mainType.initException; - this.hasChildPojoSwaps = mainType.hasChildPojoSwaps; } @SuppressWarnings({ "unchecked", "rawtypes" }) ClassMeta init() { try { - beanFilter = findBeanFilter(beanContext); - pojoSwap = findPojoSwap(beanContext); - - if (innerClass != Object.class) { - this.noArgConstructor = beanContext.getImplClassConstructor(innerClass, Visibility.PUBLIC); - if (noArgConstructor == null) - noArgConstructor = findNoArgConstructor(innerClass, Visibility.PUBLIC); - } - - this.hasChildPojoSwaps = beanContext.hasChildPojoSwaps(innerClass); Class c = innerClass; @@ -528,6 +575,35 @@ public final class ClassMeta<T> implements Type { return beanContext.findParameters(innerClass, innerClass); } + private BeanFilter findBeanFilter() { + try { + List<Bean> ba = ReflectionUtils.findAnnotations(Bean.class, innerClass); + if (! ba.isEmpty()) + return new AnnotationBeanFilterBuilder(innerClass, ba).build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + + @SuppressWarnings("unchecked") + private PojoSwap<T,?> findPojoSwap() { + try { + Pojo p = innerClass.getAnnotation(Pojo.class); + if (p != null) { + Class<?> c = p.swap(); + if (c != Null.class) { + if (ClassUtils.isParentClass(PojoSwap.class, c)) + return (PojoSwap<T,?>)c.newInstance(); + throw new RuntimeException("TODO - Surrogate classes not yet supported."); + } + } + return null; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** * Returns the bean dictionary name associated with this class. * <p> @@ -597,45 +673,58 @@ public final class ClassMeta<T> implements Type { * * @return <jk>true</jk> if this class or any child classes has a {@link PojoSwap} associated with it. */ - public boolean hasChildPojoSwaps() { - return hasChildPojoSwaps; + protected boolean hasChildPojoSwaps() { + return childPojoSwaps != null; } - private BeanFilter findBeanFilter(BeanContext context) { - try { - if (context == null) + /** + * Returns the {@link PojoSwap} where the specified class is the same/subclass of the normal class of + * one of the child pojo swaps associated with this class. + * + * @param normalClass The normal class being resolved. + * @return The resolved {@link PojoSwap} or <jk>null</jk> if none were found. + */ + protected PojoSwap<?,?> getChildPojoSwapForSwap(Class<?> normalClass) { + if (childSwapMap != null) { + PojoSwap<?,?> s = childSwapMap.get(normalClass); + if (s == null) { + for (PojoSwap<?,?> f : childPojoSwaps) + if (s == null && isParentClass(f.getNormalClass(), normalClass)) + s = f; + if (s == null) + s = PojoSwap.NULL; + childSwapMap.putIfAbsent(normalClass, s); + } + if (s == PojoSwap.NULL) return null; - BeanFilter f = context.findBeanFilter(innerClass); - if (f != null) - return f; - List<Bean> ba = ReflectionUtils.findAnnotations(Bean.class, innerClass); - if (! ba.isEmpty()) - f = new AnnotationBeanFilterBuilder(innerClass, ba).build(); - return f; - } catch (Exception e) { - throw new RuntimeException(e); + return s; } + return null; } - @SuppressWarnings("unchecked") - private PojoSwap<T,?> findPojoSwap(BeanContext context) { - try { - Pojo p = innerClass.getAnnotation(Pojo.class); - if (p != null) { - Class<?> c = p.swap(); - if (c != Null.class) { - if (ClassUtils.isParentClass(PojoSwap.class, c)) - return (PojoSwap<T,?>)c.newInstance(); - throw new RuntimeException("TODO - Surrogate classes not yet supported."); - } + /** + * Returns the {@link PojoSwap} where the specified class is the same/subclass of the swap class of + * one of the child pojo swaps associated with this class. + * + * @param swapClass The swap class being resolved. + * @return The resolved {@link PojoSwap} or <jk>null</jk> if none were found. + */ + protected PojoSwap<?,?> getChildPojoSwapForUnswap(Class<?> swapClass) { + if (childUnswapMap != null) { + PojoSwap<?,?> s = childUnswapMap.get(swapClass); + if (s == null) { + for (PojoSwap<?,?> f : childPojoSwaps) + if (s == null && isParentClass(f.getSwapClass(), swapClass)) + s = f; + if (s == null) + s = PojoSwap.NULL; + childUnswapMap.putIfAbsent(swapClass, s); } - if (context == null) + if (s == PojoSwap.NULL) return null; - PojoSwap<T,?> f = context.findPojoSwap(innerClass); - return f; - } catch (Exception e) { - throw new RuntimeException(e); + return s; } + return null; } /** @@ -1512,31 +1601,31 @@ public final class ClassMeta<T> implements Type { public int hashCode() { return super.hashCode(); } - - public abstract static class CreateSession { - LinkedList<ClassMeta<?>> stack; - - public CreateSession push(ClassMeta<?> cm) { - if (stack == null) - stack = new LinkedList<ClassMeta<?>>(); - stack.add(cm); - return this; - } - - public CreateSession pop(ClassMeta<?> expected) { - if (stack == null || stack.removeLast() != expected) - throw new BeanRuntimeException("ClassMetaSession creation stack corruption!"); - return this; - } - - public <T> ClassMeta<T> getClassMeta(Class<T> c) { - if (stack != null) - for (ClassMeta<?> cm : stack) - if (cm.innerClass == c) - return (ClassMeta<T>)cm; - return createClassMeta(c); - } - - public abstract <T> ClassMeta<T> createClassMeta(Class<T> c); - } +// +// public abstract static class CreateSession { +// LinkedList<ClassMeta<?>> stack; +// +// public CreateSession push(ClassMeta<?> cm) { +// if (stack == null) +// stack = new LinkedList<ClassMeta<?>>(); +// stack.add(cm); +// return this; +// } +// +// public CreateSession pop(ClassMeta<?> expected) { +// if (stack == null || stack.removeLast() != expected) +// throw new BeanRuntimeException("ClassMetaSession creation stack corruption!"); +// return this; +// } +// +// public <T> ClassMeta<T> getClassMeta(Class<T> c) { +// if (stack != null) +// for (ClassMeta<?> cm : stack) +// if (cm.innerClass == c) +// return (ClassMeta<T>)cm; +// return createClassMeta(c); +// } +// +// public abstract <T> ClassMeta<T> createClassMeta(Class<T> c); +// } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java b/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java index ec97dd8..5a1d104 100644 --- a/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java +++ b/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java @@ -98,6 +98,12 @@ import org.apache.juneau.serializer.*; */ public abstract class PojoSwap<T,S> { + /** + * Represents a non-existent pojo swap. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public final static PojoSwap NULL = new PojoSwap(null, null) {}; + private final Class<T> normalClass; private final Class<?> swapClass; private ClassMeta<?> swapClassMeta;
