You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 00:08:20 UTC
[31/51] [partial] incubator-juneau git commit: Initial Juno contents
from IBM JazzHub repo
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextFactory.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextFactory.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextFactory.java
new file mode 100755
index 0000000..e69a32b
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextFactory.java
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import static com.ibm.juno.core.BeanContextProperties.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.locks.*;
+
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.json.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.utils.ClassUtils.ClassComparator;
+
+/**
+ * Factory class for creating instances of {@link BeanContext}.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class BeanContextFactory extends Lockable {
+
+ //--------------------------------------------------------------------------------
+ // Static constants
+ //--------------------------------------------------------------------------------
+
+ /**
+ * The default package pattern exclusion list.
+ * Any beans in packages in this list will not be considered beans.
+ */
+ private static final String DEFAULT_NOTBEAN_PACKAGES =
+ "java.lang,java.lang.annotation,java.lang.ref,java.lang.reflect,java.io,java.net,java.nio.*,java.util.*";
+
+ /**
+ * The default bean class exclusion list.
+ * Anything in this list will not be considered beans.
+ */
+ private static final Class<?>[] DEFAULT_NOTBEAN_CLASSES = {
+ Map.class,
+ Collection.class,
+ Reader.class,
+ Writer.class,
+ InputStream.class,
+ OutputStream.class,
+ Throwable.class
+ };
+
+ //--------------------------------------------------------------------------------
+ // Properties
+ //--------------------------------------------------------------------------------
+
+ boolean
+ beansRequireDefaultConstructor = false,
+ beansRequireSerializable = false,
+ beansRequireSettersForGetters = false,
+ beansRequireSomeProperties = true,
+ beanMapPutReturnsOldValue = false,
+ useInterfaceProxies = true,
+ ignoreUnknownBeanProperties = false,
+ ignoreUnknownNullBeanProperties = true,
+ ignorePropertiesWithoutSetters = true,
+ ignoreInvocationExceptionsOnGetters = false,
+ ignoreInvocationExceptionsOnSetters = false,
+ useJavaBeanIntrospector = false;
+
+ Set<String> notBeanPackages = new TreeSet<String>();
+ Set<Class<?>> notBeanClasses = newClassTreeSet();
+ Map<Class<?>,Class<?>> implClasses = newClassTreeMap();
+ Map<String,String> uriVars = new TreeMap<String,String>();
+ LinkedList<Class<?>> filters = new LinkedList<Class<?>>();
+ ClassLoader classLoader = null;
+
+ Visibility
+ beanConstructorVisibility = Visibility.PUBLIC,
+ beanClassVisibility = Visibility.PUBLIC,
+ beanFieldVisibility = Visibility.PUBLIC,
+ beanMethodVisibility = Visibility.PUBLIC;
+
+ // Optional default parser set by setDefaultParser().
+ ReaderParser defaultParser = null;
+
+ // Read-write lock for preventing acess to the getBeanContext() method while this factory is being modified.
+ private ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ // Current BeanContext instance.
+ private BeanContext beanContext;
+
+ //--------------------------------------------------------------------------------
+ // Methods
+ //--------------------------------------------------------------------------------
+
+ /**
+ * Default constructor.
+ */
+ public BeanContextFactory() {
+ addNotBeanClasses(DEFAULT_NOTBEAN_CLASSES);
+ setProperty(BEAN_addNotBeanPackages, DEFAULT_NOTBEAN_PACKAGES);
+ }
+
+ /**
+ * Creates and returns a {@link BeanContext} with settings currently specified on this factory class.
+ * This method will return the same object until the factory settings are modified at which point
+ * a new {@link BeanContext} will be constructed.
+ *
+ * @return The bean context object.
+ */
+ public BeanContext getBeanContext() {
+ readLock();
+ try {
+ if (beanContext == null)
+ beanContext = new BeanContext(this);
+ return beanContext;
+ } finally {
+ readUnlock();
+ }
+ }
+
+ /**
+ * Sets the default parser for this bean context.
+ * <p>
+ * The default parser is used in the following methods:
+ * <ul>
+ * <code>beanContext.newBeanMap(Bean.<jk>class</jk>).load(String)</code> - Used for parsing init properties.
+ * <li>{@link BeanContext#convertToType(Object, ClassMeta)} - Used for converting strings to beans.
+ * </ul>
+ *
+ * @param defaultParser The new default parser.
+ * @return This object (for method chaining).
+ */
+ public BeanContextFactory setDefaultParser(ReaderParser defaultParser) {
+ writeLock();
+ try {
+ this.defaultParser = defaultParser;
+ return this;
+ } finally {
+ writeUnlock();
+ }
+ }
+
+
+ //--------------------------------------------------------------------------------
+ // Configuration property methods
+ //--------------------------------------------------------------------------------
+
+ /**
+ * Sets a property on this context.
+ * <p>
+ * Refer to {@link BeanContextProperties} for a description of available properties.
+ *
+ * @param property The property whose value is getting changed.
+ * @param value The new value.
+ * @throws LockedException If {@link #lock()} was called on this object.
+ * @return This object (for method chaining).
+ */
+ public BeanContextFactory setProperty(String property, Object value) throws LockedException {
+ writeLock();
+ try {
+ // Note: Have to use the default bean context to set these properties since calling
+ // convertToType will cause the cache object to be initialized.
+ BeanContext bc = BeanContext.DEFAULT;
+
+ if (property.equals(BEAN_beansRequireDefaultConstructor))
+ beansRequireDefaultConstructor = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_beansRequireSerializable))
+ beansRequireSerializable = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_beansRequireSettersForGetters))
+ beansRequireSettersForGetters = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_beansRequireSomeProperties))
+ beansRequireSomeProperties = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_beanMapPutReturnsOldValue))
+ beanMapPutReturnsOldValue = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_beanConstructorVisibility))
+ beanConstructorVisibility = Visibility.valueOf(value.toString());
+ else if (property.equals(BEAN_beanClassVisibility))
+ beanClassVisibility = Visibility.valueOf(value.toString());
+ else if (property.equals(BEAN_beanFieldVisibility))
+ beanFieldVisibility = Visibility.valueOf(value.toString());
+ else if (property.equals(BEAN_methodVisibility))
+ beanMethodVisibility = Visibility.valueOf(value.toString());
+ else if (property.equals(BEAN_useJavaBeanIntrospector))
+ useJavaBeanIntrospector = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_useInterfaceProxies))
+ useInterfaceProxies = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_ignoreUnknownBeanProperties))
+ ignoreUnknownBeanProperties = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_ignoreUnknownNullBeanProperties))
+ ignoreUnknownNullBeanProperties = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_ignorePropertiesWithoutSetters))
+ ignorePropertiesWithoutSetters = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_ignoreInvocationExceptionsOnGetters))
+ ignoreInvocationExceptionsOnGetters = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_ignoreInvocationExceptionsOnSetters))
+ ignoreInvocationExceptionsOnSetters = bc.convertToType(value, Boolean.class);
+ else if (property.equals(BEAN_addNotBeanPackages)) {
+ Set<String> set = new TreeSet<String>(notBeanPackages);
+ for (String s : value.toString().split(","))
+ set.add(s.trim());
+ notBeanPackages = set;
+ } else if (property.equals(BEAN_removeNotBeanPackages)) {
+ Set<String> set = new TreeSet<String>(notBeanPackages);
+ for (String s : value.toString().split(","))
+ set.remove(s.trim());
+ notBeanPackages = set;
+ }
+ } finally {
+ writeUnlock();
+ }
+ return this;
+ }
+
+ /**
+ * Sets multiple properties on this context.
+ * <p>
+ * Refer to {@link BeanContextProperties} for a description of available properties.
+ *
+ * @param properties The properties to set. Ignored if <jk>null</jk>.
+ * @throws LockedException If {@link #lock()} was called on this object.
+ * @return This object (for method chaining).
+ */
+ public BeanContextFactory setProperties(ObjectMap properties) throws LockedException {
+ writeLock();
+ try {
+ if (properties != null)
+ for (Map.Entry<String,Object> e : properties.entrySet())
+ setProperty(e.getKey(), e.getValue());
+ return this;
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Adds an explicit list of Java classes to be excluded from consideration as being beans.
+ *
+ * @param classes One or more fully-qualified Java class names.
+ * @return This object (for method chaining).
+ */
+ public BeanContextFactory addNotBeanClasses(Class<?>...classes) {
+ writeLock();
+ try {
+ this.notBeanClasses.addAll(Arrays.asList(classes));
+ return this;
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Add filters to this context.
+ * <p>
+ * There are two category of classes that can be passed in through this method:
+ * <ul>
+ * <li>Subclasses of {@link PojoFilter} and {@link BeanFilter}.
+ * <li>Any other class.
+ * </ul>
+ * <p>
+ * When <code>IFilter</code> classes are specified, they identify objects that need to be
+ * transformed into some other type during serialization (and optionally the reverse during parsing).
+ * <p>
+ * When non-<code>IFilter</code> classes are specified, they are wrapped inside {@link BeanFilter BeanFilters}.
+ * For example, if you have an interface <code>IFoo</code> and a subclass <code>Foo</code>, and you
+ * only want properties defined on <code>IFoo</code> to be visible as bean properties for <code>Foo</code> objects,
+ * you can simply pass in <code>IFoo.<jk>class</jk></code> to this method.
+ * <p>
+ * The following code shows the order in which filters are applied:
+ * <p class='bcode'>
+ * <jc>// F3,F4,F1,F2</jc>
+ * beanContext.addFilters(F1.<jk>class</jk>,F2.<jk>class</jk>);
+ * beanContext.addFilters(F3.<jk>class</jk>,F4.<jk>class</jk>);
+ * </p>
+ *
+ * @param classes One or more classes to add as filters to this context.
+ * @throws LockedException If {@link #lock()} was called on this object.
+ * @return This object (for method chaining).
+ */
+ public BeanContextFactory addFilters(Class<?>...classes) throws LockedException {
+ writeLock();
+ try {
+ classes = Arrays.copyOf(classes, classes.length); // Copy array to prevent modification!
+ Collections.reverse(Arrays.asList(classes));
+ for (Class<?> c : classes)
+ filters.addFirst(c);
+ return this;
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Specifies an implementation class for an interface or abstract class.
+ * <p>
+ * For interfaces and abstract classes this method can be used to specify an implementation
+ * class for the interface/abstract class so that instances of the implementation
+ * class are used when instantiated (e.g. during a parse).
+ *
+ * @param <T> The interface class.
+ * @param interfaceClass The interface class.
+ * @param implClass The implementation of the interface class.
+ * @throws LockedException If {@link #lock()} was called on this object.
+ * @return This object (for method chaining).
+ */
+ public <T> BeanContextFactory addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException {
+ writeLock();
+ try {
+ this.implClasses.put(interfaceClass, implClass);
+ return this;
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Specifies the classloader to use when resolving classes, usually <js>"_class"</js> attributes.
+ * <p>
+ * Can be used for resolving class names when the classes being created are in a different
+ * classloader from the Juno code.
+ * <p>
+ * If <jk>null</jk>, <code>Class.forName(String)</code> will be used to resolve classes.
+ *
+ * @param classLoader The new classloader.
+ * @throws LockedException If {@link #lock()} was called on this object.
+ * @return This object (for method chaining).
+ */
+ public BeanContextFactory setClassLoader(ClassLoader classLoader) throws LockedException {
+ writeLock();
+ try {
+ this.classLoader = classLoader;
+ return this;
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+ // Overridden methods on Lockable
+ //--------------------------------------------------------------------------------
+
+ @Override /* Lockable */
+ public BeanContextFactory lock() {
+ if (! isLocked()) {
+ writeLock();
+ super.lock();
+ try {
+ notBeanPackages = Collections.unmodifiableSet(notBeanPackages);
+ notBeanClasses = Collections.unmodifiableSet(notBeanClasses);
+ implClasses = Collections.unmodifiableMap(implClasses);
+ uriVars = Collections.unmodifiableMap(uriVars);
+ } finally {
+ writeUnlock();
+ }
+ }
+ return this;
+ }
+
+ private void writeLock() {
+ checkLock();
+ lock.writeLock().lock();
+ beanContext = null;
+ }
+
+ private void writeUnlock() {
+ lock.writeLock().unlock();
+ }
+
+ private void readLock() {
+ lock.readLock().lock();
+ }
+
+ private void readUnlock() {
+ lock.readLock().unlock();
+ }
+
+ @Override /* Lockable */
+ public synchronized BeanContextFactory clone() {
+ try {
+ return (BeanContextFactory)super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e); // Shouldn't happen.
+ }
+ }
+
+ @Override /* Lockable */
+ public void onUnclone() {
+ readLock();
+ try {
+ notBeanPackages = new LinkedHashSet<String>(notBeanPackages);
+ filters = new LinkedList<Class<?>>(filters);
+ notBeanClasses = newClassTreeSet(notBeanClasses);
+ implClasses = newClassTreeMap(implClasses);
+ uriVars = new TreeMap<String,String>(uriVars);
+ beanContext = null;
+ } finally {
+ readUnlock();
+ }
+ }
+
+ @Override /* Object */
+ public String toString() {
+ readLock();
+ try {
+ ObjectMap m = new ObjectMap()
+ .append("id", System.identityHashCode(this))
+ .append("beansRequireDefaultConstructor", beansRequireDefaultConstructor)
+ .append("beansRequireSerializable", beansRequireSerializable)
+ .append("beansRequireSettersForGetters", beansRequireSettersForGetters)
+ .append("beansRequireSomeProperties", beansRequireSomeProperties)
+ .append("beanMapPutReturnsOldValue", beanMapPutReturnsOldValue)
+ .append("useInterfaceProxies", useInterfaceProxies)
+ .append("ignoreUnknownBeanProperties", ignoreUnknownBeanProperties)
+ .append("ignoreUnknownNullBeanProperties", ignoreUnknownNullBeanProperties)
+ .append("ignorePropertiesWithoutSetters", ignorePropertiesWithoutSetters)
+ .append("ignoreInvocationExceptionsOnGetters", ignoreInvocationExceptionsOnGetters)
+ .append("ignoreInvocationExceptionsOnSetters", ignoreInvocationExceptionsOnSetters)
+ .append("useJavaBeanIntrospector", useJavaBeanIntrospector)
+ .append("filters", filters)
+ .append("notBeanPackages", notBeanPackages)
+ .append("notBeanClasses", notBeanClasses)
+ .append("implClasses", implClasses)
+ .append("uriVars", uriVars);
+ return m.toString(JsonSerializer.DEFAULT_LAX_READABLE);
+ } catch (SerializeException e) {
+ return e.getLocalizedMessage();
+ } finally {
+ readUnlock();
+ }
+ }
+
+ private TreeMap<Class<?>,Class<?>> newClassTreeMap(Map<Class<?>,Class<?>> m) {
+ TreeMap<Class<?>,Class<?>> tm = newClassTreeMap();
+ tm.putAll(m);
+ return tm;
+ }
+
+ private TreeMap<Class<?>,Class<?>> newClassTreeMap() {
+ return new TreeMap<Class<?>,Class<?>>(new ClassComparator());
+ }
+
+ private TreeSet<Class<?>> newClassTreeSet(Set<Class<?>> s) {
+ TreeSet<Class<?>> ts = newClassTreeSet();
+ ts.addAll(s);
+ return ts;
+ }
+
+ private TreeSet<Class<?>> newClassTreeSet() {
+ return new TreeSet<Class<?>>(new ClassComparator());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.class
new file mode 100755
index 0000000..652c6b4
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.java
new file mode 100755
index 0000000..4edaf17
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanContextProperties.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import java.beans.*;
+import java.io.*;
+import java.lang.reflect.*;
+
+import com.ibm.juno.core.annotation.*;
+
+/**
+ * Configurable properties on the {@link BeanContextFactory} class.
+ * <p>
+ * Use the {@link BeanContextFactory#setProperty(String, Object)} method to set properties on
+ * bean contexts.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class BeanContextProperties {
+
+ /**
+ * Require no-arg constructor ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, a Java class must implement a default no-arg constructor to be considered a bean.
+ * <p>
+ * The {@link Bean @Bean} annotation can be used on a class to override this setting when <jk>true</jk>.
+ */
+ public static final String BEAN_beansRequireDefaultConstructor = "BeanContext.beansRequireDefaultConstructor";
+
+ /**
+ * Require {@link Serializable} interface ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, a Java class must implement the {@link Serializable} interface to be considered a bean.
+ * <p>
+ * The {@link Bean @Bean} annotation can be used on a class to override this setting when <jk>true</jk>.
+ */
+ public static final String BEAN_beansRequireSerializable = "BeanContext.beansRequireSerializable";
+
+ /**
+ * Require setters for getters ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, only getters that have equivalent setters will be considered as properties on a bean.
+ * Otherwise, they will be ignored.
+ */
+ public static final String BEAN_beansRequireSettersForGetters = "BeanContext.beansRequireSettersForGetters";
+
+ /**
+ * Require some properties ({@link Boolean}, default=<jk>true</jk>).
+ * <p>
+ * If <jk>true</jk>, then a Java class must contain at least 1 property to be considered a bean.
+ * <p>
+ * The {@link Bean @Bean} annotation can be used on a class to override this setting when <jk>true</jk>.
+ */
+ public static final String BEAN_beansRequireSomeProperties = "BeanContext.beansRequireSomeProperties";
+
+ /**
+ * Put returns old value ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, then the {@link BeanMap#put(String,Object) BeanMap.put()} method will return old property values.
+ * <p>
+ * Disabled by default because it introduces a slight performance penalty.
+ */
+ public static final String BEAN_beanMapPutReturnsOldValue = "BeanContext.beanMapPutReturnsOldValue";
+
+ /**
+ * Look for bean constructors with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+ */
+ public static final String BEAN_beanConstructorVisibility = "BeanContext.beanConstructorVisibility";
+
+ /**
+ * Look for bean classes with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+ * <p>
+ * Classes are not considered beans unless they meet the minimum visibility requirements.
+ * For example, if the visibility is <code>PUBLIC</code> and the bean class is <jk>protected</jk>, then
+ * the class will not be interpreted as a bean class.
+ */
+ public static final String BEAN_beanClassVisibility = "BeanContext.beanClassVisibility";
+
+ /**
+ * Look for bean fields with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+ * <p>
+ * Fields are not considered bean properties unless they meet the minimum visibility requirements.
+ * For example, if the visibility is <code>PUBLIC</code> and the bean field is <jk>protected</jk>, then
+ * the field will not be interpreted as a bean property.
+ * <p>
+ * Use {@link Visibility#NONE} to prevent bean fields from being interpreted as bean properties altogether.
+ */
+ public static final String BEAN_beanFieldVisibility = "BeanContext.beanFieldVisibility";
+
+ /**
+ * Look for bean methods with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+ * <p>
+ * Methods are not considered bean getters/setters unless they meet the minimum visibility requirements.
+ * For example, if the visibility is <code>PUBLIC</code> and the bean method is <jk>protected</jk>, then
+ * the method will not be interpreted as a bean getter or setter.
+ */
+ public static final String BEAN_methodVisibility = "BeanContext.methodVisibility";
+
+ /**
+ * Use Java {@link Introspector} for determining bean properties ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * Using the built-in Java bean introspector will not pick up fields or non-standard getters/setters.
+ * Most {@link Bean @Bean} annotations will be ignored.
+ */
+ public static final String BEAN_useJavaBeanIntrospector = "BeanContext.useJavaBeanIntrospector";
+
+ /**
+ * Use interface proxies ({@link Boolean}, default=<jk>true</jk>).
+ * <p>
+ * If <jk>true</jk>, then interfaces will be instantiated as proxy classes through the use of an {@link InvocationHandler}
+ * if there is no other way of instantiating them.
+ */
+ public static final String BEAN_useInterfaceProxies = "BeanContext.useInterfaceProxies";
+
+ /**
+ * Ignore unknown properties ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, trying to set a value on a non-existent bean property will silently be ignored.
+ * Otherwise, a {@code RuntimeException} is thrown.
+ */
+ public static final String BEAN_ignoreUnknownBeanProperties = "BeanContext.ignoreUnknownBeanProperties";
+
+ /**
+ * Ignore unknown properties with null values ({@link Boolean}, default=<jk>true</jk>).
+ * <p>
+ * If <jk>true</jk>, trying to set a <jk>null</jk> value on a non-existent bean property will silently be ignored.
+ * Otherwise, a {@code RuntimeException} is thrown.
+ */
+ public static final String BEAN_ignoreUnknownNullBeanProperties = "BeanContext.ignoreUnknownNullBeanProperties";
+
+ /**
+ * Ignore properties without setters ({@link Boolean}, default=<jk>true</jk>).
+ * <p>
+ * If <jk>true</jk>, trying to set a value on a bean property without a setter will silently be ignored.
+ * Otherwise, a {@code RuntimeException} is thrown.
+ */
+ public static final String BEAN_ignorePropertiesWithoutSetters = "BeanContext.ignorePropertiesWithoutSetters";
+
+ /**
+ * Ignore invocation errors on getters ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, errors thrown when calling bean getter methods will silently be ignored.
+ * Otherwise, a {@code BeanRuntimeException} is thrown.
+ */
+ public static final String BEAN_ignoreInvocationExceptionsOnGetters = "BeanContext.ignoreInvocationExceptionsOnGetters";
+
+ /**
+ * Ignore invocation errors on setters ({@link Boolean}, default=<jk>false</jk>).
+ * <p>
+ * If <jk>true</jk>, errors thrown when calling bean setter methods will silently be ignored.
+ * Otherwise, a {@code BeanRuntimeException} is thrown.
+ */
+ public static final String BEAN_ignoreInvocationExceptionsOnSetters = "BeanContext.ignoreInvocationExceptionsOnSetters";
+
+ /**
+ * Add to the list of packages whose classes should not be considered beans ({@link String}, comma-delimited list).
+ * <p>
+ * When specified, the current list of ignore packages are appended to.
+ * The default list of ignore packages are as follows:
+ * <ul>
+ * <li><code>java.lang</code>
+ * <li><code>java.lang.annotation</code>
+ * <li><code>java.lang.ref</code>
+ * <li><code>java.lang.reflect</code>
+ * <li><code>java.io</code>
+ * <li><code>java.net</code>
+ * <li><code>java.nio.*</code>
+ * <li><code>java.util.*</code>
+ * </ul>
+ * Any classes within these packages will be serialized to strings using {@link Object#toString()}.
+ * <p>
+ * Note that you can specify prefix patterns to include all subpackages.
+ */
+ public static final String BEAN_addNotBeanPackages = "BeanContext.addNotBeanPackages";
+
+ /**
+ * Remove from the list of packages whose classes should not be considered beans ({@link String}, comma-delimited list).
+ * <p>
+ * Essentially the opposite of {@link #BEAN_addNotBeanPackages}.
+ */
+ public static final String BEAN_removeNotBeanPackages = "BeanContext.removeNotBeanPackages";
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1$1.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1$1.class
new file mode 100755
index 0000000..a16cbe6
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1$1.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1.class
new file mode 100755
index 0000000..f6be25f
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap$1.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.class
new file mode 100755
index 0000000..61512ad
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.java
new file mode 100755
index 0000000..8aa1fb5
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMap.java
@@ -0,0 +1,461 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.xml.annotation.*;
+
+/**
+ * Java bean wrapper class.
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * A wrapper that wraps Java bean instances inside of a {@link Map} interface that allows
+ * properties on the wrapped object can be accessed using the {@link Map#get(Object) get()} and {@link Map#put(Object,Object) put()} methods.
+ * <p>
+ * Use the {@link BeanContext} class to create instances of this class.
+ *
+ *
+ * <h6 class='topic'>Bean property order</h6>
+ * <p>
+ * The order of the properties returned by the {@link Map#keySet() keySet()} and {@link Map#entrySet() entrySet()} methods are as follows:
+ * <ul>
+ * <li>If {@link Bean @Bean} annotation is specified on class, then the order is the same as the list of properties in the annotation.
+ * <li>If {@link Bean @Bean} annotation is not specified on the class, then the order is the same as that returned
+ * by the {@link java.beans.BeanInfo} class (i.e. ordered by definition in the class).
+ * </ul>
+ * <br>
+ * The order can also be overridden through the use of a {@link BeanFilter}.
+ *
+ *
+ * <h6 class='topic'>POJO filters</h6>
+ * <p>
+ * If {@link PojoFilter PojoFilters} are defined on the class types of the properties of this bean or the bean properties themselves, the
+ * {@link #get(Object)} and {@link #put(Object, Object)} methods will automatically
+ * transform the property value to and from the serialized form.
+ *
+ * @author Barry M. Caceres
+ * @author James Bognar (jbognar@us.ibm.com)
+ * @param <T> Specifies the type of object that this map encapsulates.
+ */
+public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T> {
+
+ /** The wrapped object. */
+ protected T bean;
+
+ /** Temporary holding cache for beans with read-only properties. Normally null. */
+ protected Map<String,Object> propertyCache;
+
+ /** Temporary holding cache for bean properties of array types when the add() method is being used. */
+ protected Map<String,List<?>> arrayPropertyCache;
+
+ /** The BeanMeta associated with the class of the object. */
+ protected BeanMeta<T> meta;
+
+ /**
+ * Instance of this class are instantiated through the BeanContext class.
+ *
+ * @param bean The bean to wrap inside this map.
+ * @param meta The metadata associated with the bean class.
+ */
+ protected BeanMap(T bean, BeanMeta<T> meta) {
+ this.bean = bean;
+ this.meta = meta;
+ if (meta.constructorArgs.length > 0)
+ propertyCache = new TreeMap<String,Object>();
+ }
+
+ /**
+ * Returns the metadata associated with this bean map.
+ *
+ * @return The metadata associated with this bean map.
+ */
+ public BeanMeta<T> getMeta() {
+ return meta;
+ }
+
+ /**
+ * Returns the wrapped bean object.
+ * Triggers bean creation if bean has read-only properties set through a constructor
+ * defined by the {@link BeanConstructor} annotation.
+ *
+ * @return The inner bean object.
+ */
+ public T getBean() {
+ T b = getBean(true);
+
+ // If we have any arrays that need to be constructed, do it now.
+ if (arrayPropertyCache != null) {
+ for (Map.Entry<String,List<?>> e : arrayPropertyCache.entrySet()) {
+ String key = e.getKey();
+ List<?> value = e.getValue();
+ BeanPropertyMeta<T> bpm = getPropertyMeta(key);
+ try {
+ bpm.setArray(b, value);
+ } catch (Exception e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ arrayPropertyCache = null;
+ }
+ return b;
+ }
+
+ /**
+ * Returns the wrapped bean object.
+ * <p>
+ * If <code>create</code> is <jk>false</jk>, then this method may return <jk>null</jk>
+ * if the bean has read-only properties set through a constructor
+ * defined by the {@link BeanConstructor} annotation.
+ * <p>
+ * This method does NOT always return the bean in it's final state.
+ * Array properties temporary stored as ArrayLists are not finalized
+ * until the {@link #getBean()} method is called.
+ *
+ * @param create If bean hasn't been instantiated yet, then instantiate it.
+ * @return The inner bean object.
+ */
+ public T getBean(boolean create) {
+ /** If this is a read-only bean, then we need to create it. */
+ if (bean == null && create && meta.constructorArgs.length > 0) {
+ String[] props = meta.constructorArgs;
+ Constructor<T> c = meta.constructor;
+ Object[] args = new Object[props.length];
+ for (int i = 0; i < props.length; i++)
+ args[i] = propertyCache.remove(props[i]);
+ try {
+ bean = c.newInstance(args);
+ for (Map.Entry<String,Object> e : propertyCache.entrySet())
+ put(e.getKey(), e.getValue());
+ propertyCache = null;
+ } catch (Exception e) {
+ throw new BeanRuntimeException(e);
+ }
+ }
+ return bean;
+ }
+
+ /**
+ * Returns the value of the property identified as the URI property (annotated with {@link BeanProperty#beanUri()} as <jk>true</jk>).
+ *
+ * @return The URI value, or <jk>null</jk> if no URI property exists on this bean.
+ */
+ public Object getBeanUri() {
+ BeanMeta<T> bm = getMeta();
+ return bm.hasBeanUriProperty() ? bm.getBeanUriProperty().get(this) : null;
+ }
+
+ /**
+ * Sets the bean URI property if the bean has a URI property.
+ * Ignored otherwise.
+ *
+ * @param o The bean URI object.
+ * @return If the bean context setting {@code beanMapPutReturnsOldValue} is <jk>true</jk>, then the old value of the property is returned.
+ * Otherwise, this method always returns <jk>null</jk>.
+ */
+ public Object putBeanUri(Object o) {
+ BeanMeta<T> bm = getMeta();
+ return bm.hasBeanUriProperty() ? bm.getBeanUriProperty().set(this, o) : null;
+ }
+
+ /**
+ * Sets a property on the bean.
+ * <p>
+ * If there is a {@link PojoFilter} associated with this bean property or bean property type class, then
+ * you must pass in a filtered value.
+ * For example, if the bean property type class is a {@link Date} and the bean property has the
+ * {@link com.ibm.juno.core.filters.DateFilter.ISO8601DT} filter associated with it through the
+ * {@link BeanProperty#filter() @BeanProperty.filter()} annotation, the value being passed in must be
+ * a String containing an ISO8601 date-time string value.
+ *
+ * <dl>
+ * <dt>Example:</dt>
+ * <dd>
+ * <p class='bcode'>
+ * <jc>// Construct a bean with a 'birthDate' Date field</jc>
+ * Person p = <jk>new</jk> Person();
+ *
+ * <jc>// Create a bean context and add the ISO8601 date-time filter</jc>
+ * BeanContext beanContext = <jk>new</jk> BeanContext().addFilter(DateFilter.ISO8601DT.<jk>class</jk>);
+ *
+ * <jc>// Wrap our bean in a bean map</jc>
+ * BeanMap<Person> b = beanContext.forBean(p);
+ *
+ * <jc>// Set the field</jc>
+ * myBeanMap.put(<js>"birthDate"</js>, <js>"'1901-03-03T04:05:06-5000'"</js>);
+ * </p>
+ * </dd>
+ * </dl>
+ *
+ * @param property The name of the property to set.
+ * @param value The value to set the property to.
+ * @return If the bean context setting {@code beanMapPutReturnsOldValue} is <jk>true</jk>, then the old value of the property is returned.
+ * Otherwise, this method always returns <jk>null</jk>.
+ * @throws RuntimeException if any of the following occur.
+ * <ul>
+ * <li>BeanMapEntry does not exist on the underlying object.
+ * <li>Security settings prevent access to the underlying object setter method.
+ * <li>An exception occurred inside the setter method.
+ * </ul>
+ */
+ @Override /* Map */
+ public Object put(String property, Object value) {
+ BeanPropertyMeta<T> p = meta.properties.get(property);
+ if (p == null) {
+ if (meta.ctx.ignoreUnknownBeanProperties)
+ return null;
+ if (property.equals("<uri>") && meta.uriProperty != null)
+ return meta.uriProperty.set(this, value);
+
+ // If this bean has subtypes, and we haven't set the subtype yet,
+ // store the property in a temporary cache until the bean can be instantiated.
+ // This eliminates the need for requiring that the sub type attribute be provided first.
+ if (meta.subTypeIdProperty != null) {
+ if (propertyCache == null)
+ propertyCache = new TreeMap<String,Object>();
+ return propertyCache.put(property, value);
+ }
+
+ throw new BeanRuntimeException(meta.c, "Bean property ''{0}'' not found.", property);
+ }
+ if (meta.filter != null)
+ if (meta.filter.writeProperty(this.bean, property, value))
+ return null;
+ return p.set(this, value);
+ }
+
+ /**
+ * Add a value to a collection or array property.
+ * <p>
+ * As a general rule, adding to arrays is not recommended since the array must be recreate each time
+ * this method is called.
+ *
+ * @param property Property name or child-element name (if {@link Xml#childName()} is specified).
+ * @param value The value to add to the collection or array.
+ */
+ public void add(String property, Object value) {
+ BeanPropertyMeta<T> p = meta.properties.get(property);
+ if (p == null) {
+ if (meta.ctx.ignoreUnknownBeanProperties)
+ return;
+ throw new BeanRuntimeException(meta.c, "Bean property ''{0}'' not found.", property);
+ }
+ p.add(this, value);
+ }
+
+
+ /**
+ * Gets a property on the bean.
+ * <p>
+ * If there is a {@link PojoFilter} associated with this bean property or bean property type class, then
+ * this method will return the filtered value.
+ * For example, if the bean property type class is a {@link Date} and the bean property has the
+ * {@link com.ibm.juno.core.filters.DateFilter.ISO8601DT} filter associated with it through the
+ * {@link BeanProperty#filter() @BeanProperty.filter()} annotation, this method will return a String
+ * containing an ISO8601 date-time string value.
+ *
+ * <dl>
+ * <dt>Example:</dt>
+ * <dd>
+ * <p class='bcode'>
+ * <jc>// Construct a bean with a 'birthDate' Date field</jc>
+ * Person p = <jk>new</jk> Person();
+ * p.setBirthDate(<jk>new</jk> Date(1, 2, 3, 4, 5, 6));
+ *
+ * <jc>// Create a bean context and add the ISO8601 date-time filter</jc>
+ * BeanContext beanContext = <jk>new</jk> BeanContext().addFilter(DateFilter.ISO8601DT.<jk>class</jk>);
+ *
+ * <jc>// Wrap our bean in a bean map</jc>
+ * BeanMap<Person> b = beanContext.forBean(p);
+ *
+ * <jc>// Get the field as a string (i.e. "'1901-03-03T04:05:06-5000'")</jc>
+ * String s = myBeanMap.get(<js>"birthDate"</js>);
+ * </p>
+ * </dd>
+ * </dl>
+ *
+ * @param property The name of the property to get.
+ * @throws RuntimeException if any of the following occur.
+ * <ol>
+ * <li>BeanMapEntry does not exist on the underlying object.
+ * <li>Security settings prevent access to the underlying object getter method.
+ * <li>An exception occurred inside the getter method.
+ * </ol>
+ */
+ @Override /* Map */
+ public Object get(Object property) {
+ BeanPropertyMeta<T> p = meta.properties.get(property);
+ if (p == null)
+ return null;
+ if (meta.filter != null && property != null)
+ return meta.filter.readProperty(this.bean, property.toString(), p.get(this));
+ return p.get(this);
+ }
+
+ /**
+ * Convenience method for setting multiple property values by passing in JSON (or other) text.
+ * <p>
+ * Typically the input is going to be JSON, although the actual data type
+ * depends on the default parser specified by the {@link BeanContextFactory#setDefaultParser(ReaderParser)} method
+ * on the bean context that created this map.
+ *
+ * <dl>
+ * <dt>Example:</dt>
+ * <dd>
+ * <p class='bcode'>
+ * aPersonBean.load(<js>"{name:'John Smith',age:21}"</js>)
+ * </p>
+ * </dd>
+ * </dl>
+ *
+ * @param input The text that will get parsed into a map and then added to this map.
+ * @return This object (for method chaining).
+ * @throws ParseException If the input contains a syntax error or is malformed.
+ */
+ public BeanMap<T> load(String input) throws ParseException {
+ putAll(new ObjectMap(input, this.meta.ctx.defaultParser));
+ return this;
+ }
+
+ /**
+ * Convenience method for setting multiple property values by passing in a reader.
+ *
+ * @param r The text that will get parsed into a map and then added to this map.
+ * @param p The parser to use to parse the text.
+ * @return This object (for method chaining).
+ * @throws ParseException If the input contains a syntax error or is malformed.
+ * @throws IOException Thrown by <code>Reader</code>.
+ */
+ public BeanMap<T> load(Reader r, ReaderParser p) throws ParseException, IOException {
+ putAll(new ObjectMap(r, p));
+ return this;
+ }
+
+ /**
+ * Convenience method for loading this map with the contents of the specified map.
+ * <p>
+ * Identical to {@link #putAll(Map)} except as a fluent-style method.
+ *
+ * @param entries The map containing the entries to add to this map.
+ * @return This object (for method chaining).
+ */
+ @SuppressWarnings({"unchecked","rawtypes"})
+ public BeanMap<T> load(Map entries) {
+ putAll(entries);
+ return this;
+ }
+
+ /**
+ * Returns the names of all properties associated with the bean.
+ * <p>
+ * The returned set is unmodifiable.
+ */
+ @Override /* Map */
+ public Set<String> keySet() {
+ return meta.properties.keySet();
+ }
+
+ /**
+ * Returns the specified property on this bean map.
+ * <p>
+ * Allows you to get and set an individual property on a bean without having a
+ * handle to the bean itself by using the {@link BeanMapEntry#getValue()}
+ * and {@link BeanMapEntry#setValue(Object)} methods.
+ * <p>
+ * This method can also be used to get metadata on a property by
+ * calling the {@link BeanMapEntry#getMeta()} method.
+ *
+ * @param propertyName The name of the property to look up.
+ * @return The bean property, or null if the bean has no such property.
+ */
+ public BeanMapEntry<T> getProperty(String propertyName) {
+ BeanPropertyMeta<T> p = meta.properties.get(propertyName);
+ if (p == null)
+ return null;
+ return new BeanMapEntry<T>(this, p);
+ }
+
+ /**
+ * Returns the metadata on the specified property.
+ *
+ * @param propertyName The name of the bean property.
+ * @return Metadata on the specified property, or <jk>null</jk> if that property does not exist.
+ */
+ public BeanPropertyMeta<T> getPropertyMeta(String propertyName) {
+ return meta.properties.get(propertyName);
+ }
+
+ /**
+ * Returns the {@link ClassMeta} of the wrapped bean.
+ *
+ * @return The class type of the wrapped bean.
+ */
+ @Override /* Delagate */
+ public ClassMeta<T> getClassMeta() {
+ return this.meta.getClassMeta();
+ }
+
+ /**
+ * Returns all the properties associated with the bean.
+ */
+ @Override /* Map */
+ public Set<Entry<String,Object>> entrySet() {
+
+ // Construct our own anonymous set to implement this function.
+ Set<Entry<String,Object>> s = new AbstractSet<Entry<String,Object>>() {
+
+ // Get the list of properties from the meta object.
+ // Note that the HashMap.values() method caches results, so this collection
+ // will really only be constructed once per bean type since the underlying
+ // map never changes.
+ final Collection<BeanPropertyMeta<T>> pSet = meta.properties.values();
+
+ @Override /* Set */
+ public Iterator<java.util.Map.Entry<String, Object>> iterator() {
+
+ // Construct our own anonymous iterator that uses iterators against the meta.properties
+ // map to maintain position. This prevents us from having to construct any of our own
+ // collection objects.
+ return new Iterator<Entry<String,Object>>() {
+
+ final Iterator<BeanPropertyMeta<T>> pIterator = pSet.iterator();
+
+ @Override /* Iterator */
+ public boolean hasNext() {
+ return pIterator.hasNext();
+ }
+
+ @Override /* Iterator */
+ public Map.Entry<String, Object> next() {
+ return new BeanMapEntry<T>(BeanMap.this, pIterator.next());
+ }
+
+ @Override /* Iterator */
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove item from iterator.");
+ }
+ };
+ }
+
+ @Override /* Set */
+ public int size() {
+ return pSet.size();
+ }
+ };
+
+ return s;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.class
new file mode 100755
index 0000000..eb1afcc
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.java
new file mode 100755
index 0000000..c502776
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMapEntry.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import java.util.*;
+
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.filter.*;
+
+/**
+ * Represents a single entry in a bean map.
+ * <p>
+ * This class can be used to get and set property values on a bean, or to get metadata on a property.
+ *
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ * <jc>// Construct a new bean</jc>
+ * Person p = <jk>new</jk> Person();
+ *
+ * <jc>// Wrap it in a bean map</jc>
+ * BeanMap<Person> b = BeanContext.<jsf>DEFAULT</jsf>.forBean(p);
+ *
+ * <jc>// Get a reference to the birthDate property</jc>
+ * BeanMapEntry birthDate = b.getProperty(<js>"birthDate"</js>);
+ *
+ * <jc>// Set the property value</jc>
+ * birthDate.setValue(<jk>new</jk> Date(1, 2, 3, 4, 5, 6));
+ *
+ * <jc>// Or if the DateFilter.DEFAULT_ISO8601DT is registered with the bean context, set a filtered value</jc>
+ * birthDate.setFilteredValue(<js>"'1901-03-03T04:05:06-5000'"</js>);
+ * </p>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ *
+ * @param <T> The bean type.
+ */
+public class BeanMapEntry<T> implements Map.Entry<String,Object> {
+ private final BeanMap<T> beanMap;
+ private final BeanPropertyMeta<T> meta;
+
+ /**
+ * Constructor.
+ *
+ * @param beanMap The bean map that this entry belongs to.
+ * @param property The bean property.
+ */
+ protected BeanMapEntry(BeanMap<T> beanMap, BeanPropertyMeta<T> property) {
+ this.beanMap = beanMap;
+ this.meta = property;
+ }
+
+ @Override /* Map.Entry */
+ public String getKey() {
+ return meta.getName();
+ }
+
+ /**
+ * Returns the value of this property.
+ * <p>
+ * If there is a {@link PojoFilter} associated with this bean property or bean property type class, then
+ * this method will return the filtered value.
+ * For example, if the bean property type class is a {@link Date} and the bean property has the
+ * {@link com.ibm.juno.core.filters.DateFilter.ISO8601DT} filter associated with it through the
+ * {@link BeanProperty#filter() @BeanProperty.filter()} annotation, this method will return a String
+ * containing an ISO8601 date-time string value.
+ */
+ @Override /* Map.Entry */
+ public Object getValue() {
+ return meta.get(this.beanMap);
+ }
+
+ /**
+ * Sets the value of this property.
+ * <p>
+ * If the property is an array of type {@code X}, then the value can be a {@code Collection<X>} or {@code X[]} or {@code Object[]}.
+ * <p>
+ * If the property is a bean type {@code X}, then the value can either be an {@code X} or a {@code Map}.
+ * <p>
+ * If there is a {@link PojoFilter} associated with this bean property or bean property type class, then
+ * you must pass in a filtered value.
+ * For example, if the bean property type class is a {@link Date} and the bean property has the
+ * {@link com.ibm.juno.core.filters.DateFilter.ISO8601DT} filter associated with it through the
+ * {@link BeanProperty#filter() @BeanProperty.filter()} annotation, the value being passed in must be
+ * a String containing an ISO8601 date-time string value.
+ *
+ * @return The set value after it's been converted.
+ */
+ @Override /* Map.Entry */
+ public Object setValue(Object value) {
+ return meta.set(this.beanMap, value);
+ }
+
+ /**
+ * Returns the bean map that contains this property.
+ *
+ * @return The bean map that contains this property.
+ */
+ public BeanMap<T> getBeanMap() {
+ return this.beanMap;
+ }
+
+ /**
+ * Returns the metadata about this bean property.
+ *
+ * @return Metadata about this bean property.
+ */
+ public BeanPropertyMeta<T> getMeta() {
+ return this.meta;
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return this.getKey() + "=" + this.getValue();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$BeanMethod.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$BeanMethod.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$BeanMethod.class
new file mode 100755
index 0000000..a4cbf3a
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$BeanMethod.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$SubTypePropertyMeta.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$SubTypePropertyMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$SubTypePropertyMeta.class
new file mode 100755
index 0000000..2a98356
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta$SubTypePropertyMeta.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.class
new file mode 100755
index 0000000..c6c554a
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.java
new file mode 100755
index 0000000..d5ac436
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMeta.java
@@ -0,0 +1,749 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import static com.ibm.juno.core.Visibility.*;
+import static com.ibm.juno.core.utils.ClassUtils.*;
+
+import java.beans.*;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.html.*;
+import com.ibm.juno.core.jena.*;
+import com.ibm.juno.core.utils.*;
+import com.ibm.juno.core.xml.*;
+
+
+/**
+ * Encapsulates all access to the properties of a bean class (like a souped-up {@link java.beans.BeanInfo}).
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * Uses introspection to find all the properties associated with this class. If the {@link Bean @Bean} annotation
+ * is present on the class, or the class has a {@link BeanFilter} registered with it in the bean context,
+ * then that information is used to determine the properties on the class.
+ * Otherwise, the {@code BeanInfo} functionality in Java is used to determine the properties on the class.
+ *
+ *
+ * <h6 class='topic'>Bean property ordering</h6>
+ * <p>
+ * The order of the properties are as follows:
+ * <ul>
+ * <li>If {@link Bean @Bean} annotation is specified on class, then the order is the same as the list of properties in the annotation.
+ * <li>If {@link Bean @Bean} annotation is not specified on the class, then the order is based on the following.
+ * <ul>
+ * <li>Public fields (same order as {@code Class.getFields()}).
+ * <li>Properties returned by {@code BeanInfo.getPropertyDescriptors()}.
+ * <li>Non-standard getters/setters with {@link BeanProperty @BeanProperty} annotation defined on them.
+ * </ul>
+ * </ul>
+ * <br>
+ * The order can also be overridden through the use of an {@link BeanFilter}.
+ *
+ *
+ * @param <T> The class type that this metadata applies to.
+ * @author Barry M. Caceres
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public class BeanMeta<T> {
+
+ /** The target class type that this meta object describes. */
+ protected ClassMeta<T> classMeta;
+
+ /** The target class that this meta object describes. */
+ protected Class<T> c;
+
+ /** The properties on the target class. */
+ protected Map<String,BeanPropertyMeta<T>> properties;
+
+ /** The getter properties on the target class. */
+ protected Map<Method,String> getterProps = new HashMap<Method,String>();
+
+ /** The setter properties on the target class. */
+ protected Map<Method,String> setterProps = new HashMap<Method,String>();
+
+ /** The bean context that created this metadata object. */
+ protected BeanContext ctx;
+
+ /** Optional bean filter associated with the target class. */
+ protected BeanFilter<? extends T> filter;
+
+ /** Type variables implemented by this bean. */
+ protected Map<Class<?>,Class<?>[]> typeVarImpls;
+
+ /** The constructor for this bean. */
+ protected Constructor<T> constructor;
+
+ /** For beans with constructors with BeanConstructor annotation, this is the list of constructor arg properties. */
+ protected String[] constructorArgs = new String[0];
+
+ /** XML-related metadata */
+ protected XmlBeanMeta<T> xmlMeta;
+
+ // Other fields
+ BeanPropertyMeta<T> uriProperty; // The property identified as the URI for this bean (annotated with @BeanProperty.beanUri).
+ BeanPropertyMeta<T> subTypeIdProperty; // The property indentified as the sub type differentiator property (identified by @Bean.subTypeProperty annotation).
+ PropertyNamer propertyNamer; // Class used for calculating bean property names.
+
+ BeanMeta() {}
+
+ /**
+ * Constructor.
+ *
+ * @param classMeta The target class.
+ * @param ctx The bean context that created this object.
+ * @param filter Optional bean filter associated with the target class. Can be <jk>null</jk>.
+ */
+ protected BeanMeta(ClassMeta<T> classMeta, BeanContext ctx, com.ibm.juno.core.filter.BeanFilter<? extends T> filter) {
+ this.classMeta = classMeta;
+ this.ctx = ctx;
+ this.filter = filter;
+ this.c = classMeta.getInnerClass();
+ }
+
+ /**
+ * Returns the {@link ClassMeta} of this bean.
+ *
+ * @return The {@link ClassMeta} of this bean.
+ */
+ @BeanIgnore
+ public ClassMeta<T> getClassMeta() {
+ return classMeta;
+ }
+
+ /**
+ * Initializes this bean meta, and returns an error message if the specified class is not
+ * a bean for any reason.
+ *
+ * @return Reason why this class isn't a bean, or <jk>null</jk> if no problems detected.
+ * @throws BeanRuntimeException If unexpected error occurs such as invalid annotations on the bean class.
+ */
+ @SuppressWarnings("unchecked")
+ protected String init() throws BeanRuntimeException {
+
+ try {
+ Visibility
+ conVis = ctx.beanConstructorVisibility,
+ cVis = ctx.beanClassVisibility,
+ mVis = ctx.beanMethodVisibility,
+ fVis = ctx.beanFieldVisibility;
+
+ // If @Bean.interfaceClass is specified on the parent class, then we want
+ // to use the properties defined on that class, not the subclass.
+ Class<?> c2 = (filter != null && filter.getInterfaceClass() != null ? filter.getInterfaceClass() : c);
+
+ Class<?> stopClass = (filter != null ? filter.getStopClass() : Object.class);
+ if (stopClass == null)
+ stopClass = Object.class;
+
+ Map<String,BeanPropertyMeta<T>> normalProps = new LinkedHashMap<String,BeanPropertyMeta<T>>();
+
+ /// See if this class matches one the patterns in the exclude-class list.
+ if (ctx.isNotABean(c))
+ return "Class matches exclude-class list";
+
+ if (! cVis.isVisible(c.getModifiers()))
+ return "Class is not public";
+
+ if (c.isAnnotationPresent(BeanIgnore.class))
+ return "Class is annotated with @BeanIgnore";
+
+ // Make sure it's serializable.
+ if (filter == null && ctx.beansRequireSerializable && ! isParentClass(Serializable.class, c))
+ return "Class is not serializable";
+
+ // Look for @BeanConstructor constructor.
+ for (Constructor<?> x : c.getConstructors()) {
+ if (x.isAnnotationPresent(BeanConstructor.class)) {
+ if (constructor != null)
+ throw new BeanRuntimeException(c, "Multiple instances of '@BeanConstructor' found.");
+ constructor = (Constructor<T>)x;
+ constructorArgs = x.getAnnotation(BeanConstructor.class).properties();
+ if (constructorArgs.length != x.getParameterTypes().length)
+ throw new BeanRuntimeException(c, "Number of properties defined in '@BeanConstructor' annotation does not match number of parameters in constructor.");
+ if (! setAccessible(constructor))
+ throw new BeanRuntimeException(c, "Could not set accessibility to true on method with @BeanConstructor annotation. Method=''{0}''", constructor.getName());
+ }
+ }
+
+ // If this is an interface, look for impl classes defined in the context.
+ if (constructor == null)
+ constructor = (Constructor<T>)ctx.getImplClassConstructor(c, conVis);
+
+ if (constructor == null)
+ constructor = (Constructor<T>)ClassMeta.findNoArgConstructor(c, conVis);
+
+ if (constructor == null && filter == null && ctx.beansRequireDefaultConstructor)
+ return "Class does not have the required no-arg constructor";
+
+ if (! setAccessible(constructor))
+ throw new BeanRuntimeException(c, "Could not set accessibility to true on no-arg constructor");
+
+ // Explicitly defined property names in @Bean annotation.
+ Set<String> fixedBeanProps = new LinkedHashSet<String>();
+
+ if (filter != null) {
+
+ // Get the 'properties' attribute if specified.
+ if (filter.getProperties() != null)
+ for (String p : filter.getProperties())
+ fixedBeanProps.add(p);
+
+ if (filter.getPropertyNamer() != null)
+ propertyNamer = filter.getPropertyNamer().newInstance();
+ }
+
+ if (propertyNamer == null)
+ propertyNamer = new PropertyNamerDefault();
+
+ // First populate the properties with those specified in the bean annotation to
+ // ensure that ordering first.
+ for (String name : fixedBeanProps)
+ normalProps.put(name, new BeanPropertyMeta<T>(this, name));
+
+ if (ctx.useJavaBeanIntrospector) {
+ BeanInfo bi = null;
+ if (! c2.isInterface())
+ bi = Introspector.getBeanInfo(c2, stopClass);
+ else
+ bi = Introspector.getBeanInfo(c2, null);
+ if (bi != null) {
+ for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
+ String name = pd.getName();
+ if (! normalProps.containsKey(name))
+ normalProps.put(name, new BeanPropertyMeta<T>(this, name));
+ normalProps.get(name).setGetter(pd.getReadMethod()).setSetter(pd.getWriteMethod());
+ }
+ }
+
+ } else /* Use 'better' introspection */ {
+
+ for (Field f : findBeanFields(c2, stopClass, fVis)) {
+ String name = findPropertyName(f, fixedBeanProps);
+ if (name != null) {
+ if (! normalProps.containsKey(name))
+ normalProps.put(name, new BeanPropertyMeta<T>(this, name));
+ normalProps.get(name).setField(f);
+ }
+ }
+
+ List<BeanMethod> bms = findBeanMethods(c2, stopClass, mVis, fixedBeanProps, propertyNamer);
+
+ // Iterate through all the getters.
+ for (BeanMethod bm : bms) {
+ String pn = bm.propertyName;
+ Method m = bm.method;
+ if (! normalProps.containsKey(pn))
+ normalProps.put(pn, new BeanPropertyMeta<T>(this, pn));
+ BeanPropertyMeta<?> bpm = normalProps.get(pn);
+ if (! bm.isSetter)
+ bpm.setGetter(m);
+ }
+
+ // Now iterate through all the setters.
+ for (BeanMethod bm : bms) {
+ if (bm.isSetter) {
+ BeanPropertyMeta<?> bpm = normalProps.get(bm.propertyName);
+ if (bm.matchesPropertyType(bpm))
+ bpm.setSetter(bm.method);
+ }
+ }
+ }
+
+ typeVarImpls = new HashMap<Class<?>,Class<?>[]>();
+ findTypeVarImpls(c, typeVarImpls);
+ if (typeVarImpls.isEmpty())
+ typeVarImpls = null;
+
+ // Eliminate invalid properties, and set the contents of getterProps and setterProps.
+ for (Iterator<BeanPropertyMeta<T>> i = normalProps.values().iterator(); i.hasNext();) {
+ BeanPropertyMeta<T> p = i.next();
+ try {
+ if (p.validate()) {
+
+ if (p.getGetter() != null)
+ getterProps.put(p.getGetter(), p.getName());
+
+ if (p.getSetter() != null)
+ setterProps.put(p.getSetter(), p.getName());
+
+ if (p.isBeanUri())
+ uriProperty = p;
+
+ } else {
+ i.remove();
+ }
+ } catch (ClassNotFoundException e) {
+ throw new BeanRuntimeException(c, e.getLocalizedMessage());
+ }
+ }
+
+ // Check for missing properties.
+ for (String fp : fixedBeanProps)
+ if (! normalProps.containsKey(fp))
+ throw new BeanRuntimeException(c, "The property ''{0}'' was defined on the @Bean(properties=X) annotation but was not found on the class definition.", fp);
+
+ // Mark constructor arg properties.
+ for (String fp : constructorArgs) {
+ BeanPropertyMeta<T> m = normalProps.get(fp);
+ if (m == null)
+ throw new BeanRuntimeException(c, "The property ''{0}'' was defined on the @BeanConstructor(properties=X) annotation but was not found on the class definition.", fp);
+ m.setAsConstructorArg();
+ }
+
+ // Make sure at least one property was found.
+ if (filter == null && ctx.beansRequireSomeProperties && normalProps.size() == 0)
+ return "No properties detected on bean class";
+
+ properties = new LinkedHashMap<String,BeanPropertyMeta<T>>();
+
+ if (filter != null && filter.getSubTypeProperty() != null) {
+ String subTypeProperty = filter.getSubTypeProperty();
+ this.subTypeIdProperty = new SubTypePropertyMeta(subTypeProperty, filter.getSubTypes(), normalProps.remove(subTypeProperty));
+ properties.put(subTypeProperty, this.subTypeIdProperty);
+ }
+
+ properties.putAll(normalProps);
+
+ // If a filter is defined, look for inclusion and exclusion lists.
+ if (filter != null) {
+
+ // Eliminated excluded properties if BeanFilter.excludeKeys is specified.
+ String[] includeKeys = filter.getProperties();
+ String[] excludeKeys = filter.getExcludeProperties();
+ if (excludeKeys != null) {
+ for (String k : excludeKeys)
+ properties.remove(k);
+
+ // Only include specified properties if BeanFilter.includeKeys is specified.
+ // Note that the order must match includeKeys.
+ } else if (includeKeys != null) {
+ Map<String,BeanPropertyMeta<T>> properties2 = new LinkedHashMap<String,BeanPropertyMeta<T>>();
+ for (String k : includeKeys) {
+ if (properties.containsKey(k))
+ properties2.put(k, properties.get(k));
+ }
+ properties = properties2;
+ }
+ }
+
+ xmlMeta = new XmlBeanMeta<T>(this, null);
+
+ // We return this through the Bean.keySet() interface, so make sure it's not modifiable.
+ properties = Collections.unmodifiableMap(properties);
+
+ } catch (BeanRuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ return "Exception: " + StringUtils.getStackTrace(e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the subtype ID property of this bean if it has one.
+ * <p>
+ * The subtype id is specified using the {@link Bean#subTypeProperty()} annotation.
+ *
+ * @return The meta property for the sub type property, or <jk>null</jk> if no subtype is defined for this bean.
+ */
+ public BeanPropertyMeta<T> getSubTypeIdProperty() {
+ return subTypeIdProperty;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this bean has subtypes associated with it.
+ * Subtypes are defined using the {@link Bean#subTypes()} annotation.
+ *
+ * @return <jk>true</jk> if this bean has subtypes associated with it.
+ */
+ public boolean isSubTyped() {
+ return subTypeIdProperty != null;
+ }
+
+ /**
+ * Returns <jk>true</jk> if one of the properties on this bean is annotated with {@link BeanProperty#beanUri()} as <jk>true</jk>
+ *
+ * @return <jk>true</jk> if this bean has subtypes associated with it. <jk>true</jk> if there is a URI property associated with this bean.
+ */
+ public boolean hasBeanUriProperty() {
+ return uriProperty != null;
+ }
+
+ /**
+ * Returns the bean property marked as the URI for the bean (annotated with {@link BeanProperty#beanUri()} as <jk>true</jk>).
+ *
+ * @return The URI property, or <jk>null</jk> if no URI property exists on this bean.
+ */
+ public BeanPropertyMeta<T> getBeanUriProperty() {
+ return uriProperty;
+ }
+
+ /*
+ * Temporary getter/setter method struct.
+ */
+ private static class BeanMethod {
+ String propertyName;
+ boolean isSetter;
+ Method method;
+ Class<?> type;
+
+ BeanMethod(String propertyName, boolean isSetter, Method method) {
+ this.propertyName = propertyName;
+ this.isSetter = isSetter;
+ this.method = method;
+ if (isSetter)
+ this.type = method.getParameterTypes()[0];
+ else
+ this.type = method.getReturnType();
+ }
+
+ /*
+ * Returns true if this method matches the class type of the specified property.
+ * Only meant to be used for setters.
+ */
+ boolean matchesPropertyType(BeanPropertyMeta<?> b) {
+ if (b == null)
+ return false;
+
+ // Get the bean property type from the getter/field.
+ Class<?> pt = null;
+ if (b.getGetter() != null)
+ pt = b.getGetter().getReturnType();
+ else if (b.getField() != null)
+ pt = b.getField().getType();
+
+ // Doesn't match if no getter/field defined.
+ if (pt == null)
+ return false;
+
+ // Doesn't match if not same type or super type as getter/field.
+ if (! isParentClass(type, pt))
+ return false;
+
+ // If a setter was previously set, only use this setter if it's a closer
+ // match (e.g. prev type is a superclass of this type).
+ if (b.getSetter() == null)
+ return true;
+
+ Class<?> prevType = b.getSetter().getParameterTypes()[0];
+ return isParentClass(prevType, type, true);
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return method.toString();
+ }
+ }
+
+ /*
+ * Find all the bean methods on this class.
+ *
+ * @param c The filtered class.
+ * @param stopClass Don't look above this class in the hierarchy.
+ * @param v The minimum method visibility.
+ * @param fixedBeanProps Only include methods whose properties are in this list.
+ * @param pn Use this property namer to determine property names from the method names.
+ */
+ private static List<BeanMethod> findBeanMethods(Class<?> c, Class<?> stopClass, Visibility v, Set<String> fixedBeanProps, PropertyNamer pn) {
+ List<BeanMethod> l = new LinkedList<BeanMethod>();
+
+ for (Class<?> c2 : findClasses(c, stopClass)) {
+ for (Method m : c2.getDeclaredMethods()) {
+ int mod = m.getModifiers();
+ if (Modifier.isStatic(mod) || Modifier.isTransient(mod))
+ continue;
+ if (m.isAnnotationPresent(BeanIgnore.class))
+ continue;
+ if (m.isBridge()) // This eliminates methods with covariant return types from parent classes on child classes.
+ continue;
+ if (! (v.isVisible(m) || m.isAnnotationPresent(BeanProperty.class)))
+ continue;
+ String n = m.getName();
+ Class<?>[] pt = m.getParameterTypes();
+ Class<?> rt = m.getReturnType();
+ boolean isGetter = false, isSetter = false;
+ if (pt.length == 1 && n.startsWith("set") && (isParentClass(rt, c) || rt.equals(Void.TYPE))) {
+ isSetter = true;
+ n = n.substring(3);
+ } else if (pt.length == 0 && n.startsWith("get") && (! rt.equals(Void.TYPE))) {
+ isGetter = true;
+ n = n.substring(3);
+ } else if (pt.length == 0 && n.startsWith("is") && (rt.equals(Boolean.TYPE) || rt.equals(Boolean.class))) {
+ isGetter = true;
+ n = n.substring(2);
+ }
+ n = pn.getPropertyName(n);
+ if (isGetter || isSetter) {
+ BeanProperty bp = m.getAnnotation(BeanProperty.class);
+ if (bp != null && ! bp.name().equals("")) {
+ n = bp.name();
+ if (! fixedBeanProps.isEmpty())
+ if (! fixedBeanProps.contains(n))
+ throw new BeanRuntimeException(c, "Method property ''{0}'' identified in @BeanProperty, but missing from @Bean", n);
+ }
+ l.add(new BeanMethod(n, isSetter, m));
+ }
+ }
+ }
+ return l;
+ }
+
+ private static Collection<Field> findBeanFields(Class<?> c, Class<?> stopClass, Visibility v) {
+ List<Field> l = new LinkedList<Field>();
+ for (Class<?> c2 : findClasses(c, stopClass)) {
+ for (Field f : c2.getDeclaredFields()) {
+ int m = f.getModifiers();
+ if (Modifier.isStatic(m) || Modifier.isTransient(m))
+ continue;
+ if (f.isAnnotationPresent(BeanIgnore.class))
+ continue;
+ if (! (v.isVisible(f) || f.isAnnotationPresent(BeanProperty.class)))
+ continue;
+ l.add(f);
+ }
+ }
+ return l;
+ }
+
+ private static List<Class<?>> findClasses(Class<?> c, Class<?> stopClass) {
+ LinkedList<Class<?>> l = new LinkedList<Class<?>>();
+ findClasses(c, l, stopClass);
+ return l;
+ }
+
+ private static void findClasses(Class<?> c, LinkedList<Class<?>> l, Class<?> stopClass) {
+ while (c != null && stopClass != c) {
+ l.addFirst(c);
+ for (Class<?> ci : c.getInterfaces())
+ findClasses(ci, l, stopClass);
+ c = c.getSuperclass();
+ }
+ }
+
+ /**
+ * Returns the metadata on all properties associated with this bean.
+ *
+ * @return Metadata on all properties associated with this bean.
+ */
+ public Collection<BeanPropertyMeta<T>> getPropertyMetas() {
+ return this.properties.values();
+ }
+
+ /**
+ * Returns the metadata on the specified list of properties.
+ *
+ * @param pNames The list of properties to retrieve. If <jk>null</jk>, returns all properties.
+ * @return The metadata on the specified list of properties.
+ */
+ public Collection<BeanPropertyMeta<T>> getPropertyMetas(final String...pNames) {
+ if (pNames == null)
+ return getPropertyMetas();
+ List<BeanPropertyMeta<T>> l = new ArrayList<BeanPropertyMeta<T>>(pNames.length);
+ for (int i = 0; i < pNames.length; i++)
+ l.add(getPropertyMeta(pNames[i]));
+ return l;
+ }
+
+ /**
+ * Returns XML related metadata for this bean type.
+ *
+ * @return The XML metadata for this bean type.
+ */
+ public XmlBeanMeta<T> getXmlMeta() {
+ return xmlMeta;
+ }
+
+ /**
+ * Returns metadata about the specified property.
+ *
+ * @param name The name of the property on this bean.
+ * @return The metadata about the property, or <jk>null</jk> if no such property exists
+ * on this bean.
+ */
+ public BeanPropertyMeta<T> getPropertyMeta(String name) {
+ return this.properties.get(name);
+ }
+
+ /**
+ * Creates a new instance of this bean.
+ *
+ * @param outer The outer object if bean class is a non-static inner member class.
+ * @return A new instance of this bean if possible, or <jk>null</jk> if not.
+ * @throws IllegalArgumentException Thrown by constructor.
+ * @throws InstantiationException Thrown by constructor.
+ * @throws IllegalAccessException Thrown by constructor.
+ * @throws InvocationTargetException Thrown by constructor.
+ */
+ @SuppressWarnings("unchecked")
+ protected T newBean(Object outer) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ if (classMeta.isMemberClass) {
+ if (constructor != null)
+ return constructor.newInstance(outer);
+ } else {
+ if (constructor != null)
+ return constructor.newInstance((Object[])null);
+ InvocationHandler h = classMeta.getProxyInvocationHandler();
+ if (h != null) {
+ ClassLoader cl = classMeta.beanContext.classLoader;
+ if (cl == null)
+ cl = this.getClass().getClassLoader();
+ return (T)Proxy.newProxyInstance(cl, new Class[] { classMeta.innerClass, java.io.Serializable.class }, h);
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Returns the property name of the specified field if it's a valid property.
+ * Returns null if the field isn't a valid property.
+ */
+ private String findPropertyName(Field f, Set<String> fixedBeanProps) {
+ BeanProperty bp = f.getAnnotation(BeanProperty.class);
+ if (bp != null && ! bp.name().equals("")) {
+ String name = bp.name();
+ if (fixedBeanProps.isEmpty() || fixedBeanProps.contains(name))
+ return name;
+ throw new BeanRuntimeException(c, "Method property ''{0}'' identified in @BeanProperty, but missing from @Bean", name);
+ }
+ String name = propertyNamer.getPropertyName(f.getName());
+ if (fixedBeanProps.isEmpty() || fixedBeanProps.contains(name))
+ return name;
+ return null;
+ }
+
+ /**
+ * Recursively determines the classes represented by parameterized types in the class hierarchy of
+ * the specified type, and puts the results in the specified map.<br>
+ * <p>
+ * For example, given the following classes...
+ * <p class='bcode'>
+ * public static class BeanA<T> {
+ * public T x;
+ * }
+ * public static class BeanB extends BeanA<Integer>} {...}
+ * <p>
+ * ...calling this method on {@code BeanB.class} will load the following data into {@code m} indicating
+ * that the {@code T} parameter on the BeanA class is implemented with an {@code Integer}:
+ * <p class='bcode'>
+ * {BeanA.class:[Integer.class]}
+ * <p>
+ * TODO: This code doesn't currently properly handle the following situation:
+ * <p class='bcode'>
+ * public static class BeanB<T extends Number> extends BeanA<T>;
+ * public static class BeanC extends BeanB<Integer>;
+ * <p>
+ * When called on {@code BeanC}, the variable will be detected as a {@code Number}, not an {@code Integer}.<br>
+ * If anyone can figure out a better way of doing this, please do so!
+ *
+ * @param t The type we're recursing.
+ * @param m Where the results are loaded.
+ */
+ private static void findTypeVarImpls(Type t, Map<Class<?>,Class<?>[]> m) {
+ if (t instanceof Class) {
+ Class<?> c = (Class<?>)t;
+ findTypeVarImpls(c.getGenericSuperclass(), m);
+ for (Type ci : c.getGenericInterfaces())
+ findTypeVarImpls(ci, m);
+ } else if (t instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType)t;
+ Type rt = pt.getRawType();
+ if (rt instanceof Class) {
+ Type[] gImpls = pt.getActualTypeArguments();
+ Class<?>[] gTypes = new Class[gImpls.length];
+ for (int i = 0; i < gImpls.length; i++) {
+ Type gt = gImpls[i];
+ if (gt instanceof Class)
+ gTypes[i] = (Class<?>)gt;
+ else if (gt instanceof TypeVariable) {
+ TypeVariable<?> tv = (TypeVariable<?>)gt;
+ for (Type upperBound : tv.getBounds())
+ if (upperBound instanceof Class)
+ gTypes[i] = (Class<?>)upperBound;
+ }
+ }
+ m.put((Class<?>)rt, gTypes);
+ findTypeVarImpls(pt.getRawType(), m);
+ }
+ }
+ }
+
+ /*
+ * Bean property for getting and setting bean subtype.
+ */
+ @SuppressWarnings({"rawtypes","unchecked"})
+ private class SubTypePropertyMeta extends BeanPropertyMeta<T> {
+
+ private Map<Class<?>,String> subTypes;
+ private BeanPropertyMeta<T> realProperty; // Bean property if bean actually has a real subtype field.
+
+ SubTypePropertyMeta(String subTypeAttr, Map<Class<?>,String> subTypes, BeanPropertyMeta<T> realProperty) {
+ super(BeanMeta.this, subTypeAttr, ctx.string());
+ this.subTypes = subTypes;
+ this.realProperty = realProperty;
+ this.htmlMeta = new HtmlBeanPropertyMeta<T>(this);
+ this.xmlMeta = new XmlBeanPropertyMeta<T>(this);
+ this.rdfMeta = new RdfBeanPropertyMeta<T>(this);
+ }
+
+ /*
+ * Setting this bean property causes the inner bean to be set to the subtype implementation.
+ */
+ @Override /* BeanPropertyMeta */
+ public Object set(BeanMap<T> m, Object value) throws BeanRuntimeException {
+ if (value == null)
+ throw new BeanRuntimeException("Attempting to set bean subtype property to null.");
+ String subTypeId = value.toString();
+ for (Entry<Class<?>,String> e : subTypes.entrySet()) {
+ if (e.getValue().equals(subTypeId)) {
+ Class subTypeClass = e.getKey();
+ m.meta = ctx.getBeanMeta(subTypeClass);
+ try {
+ m.bean = (T)subTypeClass.newInstance();
+ if (realProperty != null)
+ realProperty.set(m, value);
+ // If subtype attribute wasn't specified first, set them again from the temporary cache.
+ if (m.propertyCache != null)
+ for (Map.Entry<String,Object> me : m.propertyCache.entrySet())
+ m.put(me.getKey(), me.getValue());
+ } catch (Exception e1) {
+ throw new BeanRuntimeException(e1);
+ }
+ return null;
+ }
+ }
+ throw new BeanRuntimeException(c, "Unknown subtype ID ''{0}''", subTypeId);
+ }
+
+ @Override /* BeanPropertyMeta */
+ public Object get(BeanMap<T> m) throws BeanRuntimeException {
+ String subTypeId = filter.getSubTypes().get(c);
+ if (subTypeId == null)
+ throw new BeanRuntimeException(c, "Unmapped sub type class");
+ return subTypeId;
+ }
+ }
+
+ @Override /* Object */
+ public String toString() {
+ StringBuilder sb = new StringBuilder(c.getName());
+ sb.append(" {\n");
+ for (BeanPropertyMeta<?> pm : this.properties.values())
+ sb.append("\t").append(pm.toString()).append(",\n");
+ sb.append("}");
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.class
new file mode 100755
index 0000000..6f63839
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.java
new file mode 100755
index 0000000..9effae3
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanMetaFiltered.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import java.util.*;
+
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.xml.*;
+
+/**
+ * Sames as {@link BeanMeta}, except the list of bean properties are limited
+ * by a {@link BeanProperty#properties()} annotation.
+ *
+ * @param <T> The class type that this metadata applies to.
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class BeanMetaFiltered<T> extends BeanMeta<T> {
+
+ private final BeanMeta<T> innerMeta;
+
+ /**
+ * Wrapper constructor.
+ *
+ * @param innerMeta The unfiltered bean meta of the bean property.
+ * @param pNames The list of filtered property names.
+ */
+ public BeanMetaFiltered(BeanMeta<T> innerMeta, String[] pNames) {
+ this.innerMeta = innerMeta;
+ this.properties = new LinkedHashMap<String,BeanPropertyMeta<T>>();
+ for (String p : pNames)
+ properties.put(p, innerMeta.getPropertyMeta(p));
+ this.xmlMeta = new XmlBeanMeta<T>(innerMeta, pNames);
+ }
+
+ /**
+ * Wrapper constructor.
+ *
+ * @param innerMeta The unfiltered bean meta of the bean property.
+ * @param pNames The list of filtered property names.
+ */
+ public BeanMetaFiltered(BeanMeta<T> innerMeta, Collection<String> pNames) {
+ this(innerMeta, pNames.toArray(new String[pNames.size()]));
+ }
+
+ @Override /* Delagate */
+ public ClassMeta<T> getClassMeta() {
+ return innerMeta.classMeta;
+ }
+
+ @Override /* BeanMeta */
+ public Collection<BeanPropertyMeta<T>> getPropertyMetas() {
+ return properties.values();
+ }
+
+ @Override /* BeanMeta */
+ public BeanPropertyMeta<T> getPropertyMeta(String name) {
+ return properties.get(name);
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return innerMeta.c.getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.class
new file mode 100755
index 0000000..d55611b
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.class differ