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 2017/05/02 13:58:42 UTC

incubator-juneau git commit: "extras" property, part 1.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master 831a2bd9f -> 9caef98a9


"extras" property, part 1.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/9caef98a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/9caef98a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/9caef98a

Branch: refs/heads/master
Commit: 9caef98a90dd2e5ce02541c38d575cb310099b30
Parents: 831a2bd
Author: JamesBognar <ja...@apache.org>
Authored: Tue May 2 09:58:36 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Tue May 2 09:58:36 2017 -0400

----------------------------------------------------------------------
 .../main/java/org/apache/juneau/BeanMeta.java   |  12 +-
 .../org/apache/juneau/BeanPropertyMeta.java     | 184 ++++++++++++++-----
 2 files changed, 147 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9caef98a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
index e1edb43..a5a894f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
@@ -89,6 +89,7 @@ public class BeanMeta<T> {
 	// Other fields
 	final String typePropertyName;                                      // "_type" property actual name.
 	private final BeanPropertyMeta typeProperty;                        // "_type" mock bean property.
+	final BeanPropertyMeta extrasProperty;                              // "extras" property.
 	private final String dictionaryName;                                // The @Bean.typeName() annotation defined on this bean class.
 	final String notABeanReason;                                        // Readable string explaining why this class wasn't a bean.
 	final BeanRegistry beanRegistry;
@@ -114,6 +115,7 @@ public class BeanMeta<T> {
 		this.properties = b.properties == null ? null : Collections.unmodifiableMap(b.properties);
 		this.getterProps = Collections.unmodifiableMap(b.getterProps);
 		this.setterProps = Collections.unmodifiableMap(b.setterProps);
+		this.extrasProperty = b.extrasProperty;
 		this.typeVarImpls = b.typeVarImpls == null ? null : Collections.unmodifiableMap(b.typeVarImpls);
 		this.constructor = b.constructor;
 		this.constructorArgs = b.constructorArgs;
@@ -131,6 +133,8 @@ public class BeanMeta<T> {
 		Map<String,BeanPropertyMeta> properties;
 		Map<Method,String> getterProps = new HashMap<Method,String>();
 		Map<Method,String> setterProps = new HashMap<Method,String>();
+		BeanPropertyMeta extrasProperty;
+
 		Map<Class<?>,Class<?>[]> typeVarImpls;
 		Constructor<T> constructor;
 		String[] constructorArgs = new String[0];
@@ -346,8 +350,12 @@ public class BeanMeta<T> {
 				if (dictionaryName == null)
 					dictionaryName = findDictionaryName(this.classMeta);
 
-				for (Map.Entry<String,BeanPropertyMeta.Builder> e : normalProps.entrySet())
-					properties.put(e.getKey(), e.getValue().build());
+				for (Map.Entry<String,BeanPropertyMeta.Builder> e : normalProps.entrySet()) {
+					if ("*".equals(e.getValue().name))
+						extrasProperty = e.getValue().build();
+					else
+						properties.put(e.getKey(), e.getValue().build());
+				}
 
 				// If a beanFilter is defined, look for inclusion and exclusion lists.
 				if (beanFilter != null) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9caef98a/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 f16c7fe..99f834c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -49,6 +49,7 @@ public class BeanPropertyMeta {
 	private final Field field;                                // The bean property field (if it has one).
 	private final Method getter, setter;                      // The bean property getter and setter.
 	private final boolean isUri;                              // True if this is a URL/URI or annotated with @URI.
+	private final boolean isPropertyMap;
 
 	private final ClassMeta<?>
 		rawTypeMeta,                                           // The real class type of the bean property.
@@ -57,7 +58,7 @@ public class BeanPropertyMeta {
 	private final String[] properties;                        // The value of the @BeanProperty(properties) annotation.
 	private final PojoSwap swap;                              // PojoSwap defined only via @BeanProperty annotation.
 
-	private final MetadataMap extMeta = new MetadataMap();    // Extended metadata
+	private final MetadataMap extMeta;                        // Extended metadata
 	private final BeanRegistry beanRegistry;
 
 	private final Object overrideValue;                       // The bean property value (if it's an overridden delegate).
@@ -79,6 +80,7 @@ public class BeanPropertyMeta {
 		private BeanRegistry beanRegistry;
 		private Object overrideValue;
 		private BeanPropertyMeta delegateFor;
+		private MetadataMap extMeta = new MetadataMap();
 
 		Builder(BeanMeta<?> beanMeta, String name) {
 			this.beanMeta = beanMeta;
@@ -89,8 +91,6 @@ public class BeanPropertyMeta {
 		Builder(BeanMeta<?> beanMeta, String name, ClassMeta<?> rawTypeMeta, BeanRegistry beanRegistry) {
 			this(beanMeta, name);
 			this.rawTypeMeta = rawTypeMeta;
-			if (rawTypeMeta == null)
-				throw new RuntimeException("xxx");
 			this.typeMeta = rawTypeMeta;
 			this.beanRegistry = beanRegistry;
 		}
@@ -151,8 +151,8 @@ public class BeanPropertyMeta {
 					rawTypeMeta = f.resolveClassMeta(p, setter.getGenericParameterTypes()[0], typeVarImpls);
 				isUri |= (rawTypeMeta.isUri() || setter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
 				if (p != null) {
-				if (swap == null)
-					swap = getPropertyPojoSwap(p);
+					if (swap == null)
+						swap = getPropertyPojoSwap(p);
 					if (properties != null && ! p.properties().isEmpty())
 						properties = StringUtils.split(p.properties(), ',');
 					bdClasses.addAll(Arrays.asList(p.beanDictionary()));
@@ -164,14 +164,44 @@ public class BeanPropertyMeta {
 
 			this.beanRegistry = new BeanRegistry(beanContext, parentBeanRegistry, bdClasses.toArray(new Class<?>[0]));
 
+			boolean isAnyProperty = "*".equals(name);
+
 			// Do some annotation validation.
 			Class<?> c = rawTypeMeta.getInnerClass();
-			if (getter != null && ! isParentClass(getter.getReturnType(), c))
-				return false;
-			if (setter != null && ! isParentClass(setter.getParameterTypes()[0], c))
-				return false;
-			if (field != null && ! isParentClass(field.getType(), c))
-				return false;
+			if (getter != null) {
+				if (! isParentClass(getter.getReturnType(), c))
+					return false;
+				Class<?>[] pt = getter.getParameterTypes();
+				if (isAnyProperty) {
+					if (pt.length != 1)
+						return false;
+					if (! pt[0].equals(String.class))
+						return false;
+				}
+			}
+			if (setter != null) {
+				Class<?>[] pt = setter.getParameterTypes();
+				if (pt.length != (isAnyProperty ? 2 : 1))
+					return false;
+				if (isAnyProperty) {
+					if (pt[0].equals(String.class))
+						return false;
+					if (! isParentClass(pt[1], c))
+						return false;
+				} else {
+					if (! isParentClass(pt[0], c))
+						return false;
+				}
+			}
+			if (field != null) {
+				if (isAnyProperty) {
+					if (! isParentClass(field.getType(), Map.class))
+						return false;
+				} else {
+					if (! isParentClass(field.getType(), c))
+						return false;
+				}
+			}
 
 			if (typeMeta == null)
 				typeMeta = (swap != null ? swap.getSwapClassMeta(beanContext) : rawTypeMeta == null ? beanContext.object() : rawTypeMeta.getSerializedClassMeta());
@@ -247,6 +277,35 @@ public class BeanPropertyMeta {
 		this.beanRegistry = b.beanRegistry;
 		this.overrideValue = b.overrideValue;
 		this.delegateFor = b.delegateFor;
+		this.extMeta = b.extMeta;
+		this.isPropertyMap = false;
+	}
+
+	/**
+	 * Creates a BeanPropertyMeta for an "extras" property.
+	 * <p>
+	 * An extras property is one defined with <code><ja>@BeanProperty</ja>(name=<js>"*"</js>)</code>
+	 *
+	 * @param name The bean property name (e.g. the key if this is a Map field, or the value passed to the getter/setter as the property name).
+	 * @param b The real bean property.
+	 */
+	protected BeanPropertyMeta(String name, BeanPropertyMeta b) {
+		this.field = b.field;
+		this.getter = b.getter;
+		this.setter = b.setter;
+		this.isUri = false;
+		this.beanMeta = b.beanMeta;
+		this.beanContext = b.beanContext;
+		this.name = name;
+		this.rawTypeMeta = b.rawTypeMeta;
+		this.typeMeta = b.typeMeta;
+		this.properties = b.properties;
+		this.swap = b.swap;
+		this.beanRegistry = b.beanRegistry;
+		this.overrideValue = b.overrideValue;
+		this.delegateFor = b.delegateFor;
+		this.extMeta = b.extMeta;
+		this.isPropertyMap = true;
 	}
 
 	/**
@@ -381,10 +440,9 @@ public class BeanPropertyMeta {
 				throw new BeanRuntimeException(beanMeta.c, "Getter or public field not defined on property ''{0}''", name);
 
 			if (getter != null)
-				o = getter.invoke(bean, (Object[])null);
-
+				o = invokeGetter(bean);
 			else if (field != null)
-				o = field.get(bean);
+				o = invokeGetField(bean);
 
 			return toSerializedForm(m.getBeanSession(), o);
 
@@ -458,8 +516,8 @@ public class BeanPropertyMeta {
 				throw new BeanRuntimeException("Non-existent bean instance on bean.");
 			}
 
-				boolean isMap = rawTypeMeta.isMap();
-				boolean isCollection = rawTypeMeta.isCollection();
+			boolean isMap = rawTypeMeta.isMap();
+			boolean isCollection = rawTypeMeta.isCollection();
 
 			if (field == null && setter == null && ! (isMap || isCollection)) {
 				if ((value == null && beanContext.ignoreUnknownNullBeanProperties) || beanContext.ignorePropertiesWithoutSetters)
@@ -472,14 +530,14 @@ public class BeanPropertyMeta {
 			try {
 
 				Object r = beanContext.beanMapPutReturnsOldValue || isMap || isCollection ? get(m) : null;
-					Class<?> propertyClass = rawTypeMeta.getInnerClass();
+				Class<?> propertyClass = rawTypeMeta.getInnerClass();
 
 				if (value == null && (isMap || isCollection)) {
 					if (setter != null) {
-						setter.invoke(bean, new Object[] { null });
+						invokeSetter(bean, null);
 						return r;
 					} else if (field != null) {
-						field.set(bean, null);
+						invokeSetField(bean, null);
 						return r;
 					}
 					throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' to null because no setter or public field is defined", name);
@@ -500,7 +558,7 @@ public class BeanPropertyMeta {
 
 					// If the property type is abstract, then we either need to reuse the existing
 					// map (if it's not null), or try to assign the value directly.
-						if (! rawTypeMeta.canCreateNewInstance()) {
+					if (! rawTypeMeta.canCreateNewInstance()) {
 						if (propMap == null) {
 							if (setter == null && field == null)
 								throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' to object of type ''{2}'' because no setter or public field is defined, and the current value is null", name, propertyClass.getName(), findClassName(value));
@@ -514,9 +572,9 @@ public class BeanPropertyMeta {
 									}
 								}
 								if (setter != null)
-									setter.invoke(bean, valueMap);
+									invokeSetter(bean, valueMap);
 								else
-									field.set(bean, valueMap);
+									invokeSetField(bean, valueMap);
 								return r;
 							}
 							throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{2}'' to object of type ''{2}'' because the assigned map cannot be converted to the specified type because the property type is abstract, and the property value is currently null", name, propertyClass.getName(), findClassName(value));
@@ -525,9 +583,9 @@ public class BeanPropertyMeta {
 						if (propMap == null) {
 							propMap = (Map)propertyClass.newInstance();
 							if (setter != null)
-								setter.invoke(bean, propMap);
+								invokeSetter(bean, propMap);
 							else if (field != null)
-								field.set(bean, propMap);
+								invokeSetField(bean, propMap);
 							else
 								throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' to object of type ''{2}'' because no setter or public field is defined on this property, and the existing property value is null", name, propertyClass.getName(), findClassName(value));
 						} else {
@@ -559,7 +617,7 @@ public class BeanPropertyMeta {
 
 					// If the property type is abstract, then we either need to reuse the existing
 					// collection (if it's not null), or try to assign the value directly.
-						if (! rawTypeMeta.canCreateNewInstance()) {
+					if (! rawTypeMeta.canCreateNewInstance()) {
 						if (propList == null) {
 							if (setter == null && field == null)
 								throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' to object of type ''{2}'' because no setter or public field is defined, and the current value is null", name, propertyClass.getName(), findClassName(value));
@@ -576,9 +634,9 @@ public class BeanPropertyMeta {
 										valueList = l;
 									}
 								if (setter != null)
-									setter.invoke(bean, valueList);
+									invokeSetter(bean, valueList);
 								else
-									field.set(bean, valueList);
+									invokeSetField(bean, valueList);
 								return r;
 							}
 							throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' to object of type ''{2}'' because the assigned map cannot be converted to the specified type because the property type is abstract, and the property value is currently null", name, propertyClass.getName(), findClassName(value));
@@ -588,9 +646,9 @@ public class BeanPropertyMeta {
 						if (propList == null) {
 							propList = (Collection)propertyClass.newInstance();
 							if (setter != null)
-								setter.invoke(bean, propList);
+								invokeSetter(bean, propList);
 							else if (field != null)
-								field.set(bean, propList);
+								invokeSetField(bean, propList);
 							else
 								throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' to object of type ''{2}'' because no setter is defined on this property, and the existing property value is null", name, propertyClass.getName(), findClassName(value));
 						} else {
@@ -612,9 +670,9 @@ public class BeanPropertyMeta {
 						value = session.convertToType(value, rawTypeMeta);
 					}
 					if (setter != null)
-						setter.invoke(bean, new Object[] { value });
+						invokeSetter(bean, value);
 					else if (field != null)
-						field.set(bean, value);
+						invokeSetField(bean, value);
 				}
 
 				return r;
@@ -635,6 +693,38 @@ public class BeanPropertyMeta {
 		}
 	}
 
+	private Object invokeGetter(Object bean) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+		if (isPropertyMap)
+			return getter.invoke(bean, name);
+		return getter.invoke(bean);
+	}
+
+	private Object invokeSetter(Object bean, Object val) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+		if (isPropertyMap)
+			return setter.invoke(bean, name, val);
+		return setter.invoke(bean, val);
+	}
+
+	private Object invokeGetField(Object bean) throws IllegalArgumentException, IllegalAccessException {
+		if (isPropertyMap) {
+			Map m = (Map)field.get(bean);
+			if (m != null)
+				return m.get(name);
+			return null;
+		}
+		return field.get(bean);
+	}
+
+	private void invokeSetField(Object bean, Object val) throws IllegalArgumentException, IllegalAccessException {
+		if (isPropertyMap) {
+			Map m = (Map)field.get(bean);
+			if (m != null)
+				m.put(name, val);
+		} else {
+			field.set(bean, val);
+		}
+	}
+
 	/**
 	 * Sets an array field on this bean.
 	 * Works on both <code>Object</code> and primitive arrays.
@@ -648,9 +738,9 @@ public class BeanPropertyMeta {
 	protected void setArray(Object bean, List l) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
 		Object array = ArrayUtils.toArray(l, this.rawTypeMeta.getElementType().getInnerClass());
 		if (setter != null)
-			setter.invoke(bean, array);
+			invokeSetter(bean, array);
 		else if (field != null)
-			field.set(bean, array);
+			invokeSetField(bean, array);
 		else
 			throw new BeanRuntimeException(beanMeta.c, "Attempt to initialize array property ''{0}'', but no setter or field defined.", name);
 	}
@@ -692,9 +782,9 @@ public class BeanPropertyMeta {
 			if (isCollection) {
 				Collection c = null;
 				if (getter != null) {
-					c = (Collection)getter.invoke(bean, (Object[])null);
+					c = (Collection)invokeGetter(bean);
 				} else if (field != null) {
-					c = (Collection)field.get(bean);
+					c = (Collection)invokeGetField(bean);
 				} else {
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to append to collection property ''{0}'', but no getter or field defined.", name);
 				}
@@ -712,9 +802,9 @@ public class BeanPropertyMeta {
 				c.add(v);
 
 				if (setter != null)
-					setter.invoke(bean, c);
+					invokeSetter(bean, c);
 				else if (field != null)
-					field.set(bean, c);
+					invokeSetField(bean, c);
 				else
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to initialize collection property ''{0}'', but no setter or field defined.", name);
 
@@ -731,9 +821,9 @@ public class BeanPropertyMeta {
 					// Copy any existing array values into the temporary list.
 					Object oldArray;
 				if (getter != null)
-					oldArray = getter.invoke(bean, (Object[])null);
+					oldArray = invokeGetter(bean);
 				else if (field != null)
-					oldArray = field.get(bean);
+					oldArray = invokeGetField(bean);
 				else
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to append to array property ''{0}'', but no getter or field defined.", name);
 					ArrayUtils.copyToList(oldArray, l);
@@ -786,9 +876,9 @@ public class BeanPropertyMeta {
 			if (isMap) {
 				Map map = null;
 				if (getter != null) {
-					map = (Map)getter.invoke(bean, (Object[])null);
+					map = (Map)invokeGetter(bean);
 				} else if (field != null) {
-					map = (Map)field.get(bean);
+					map = (Map)invokeGetField(bean);
 				} else {
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to append to map property ''{0}'', but no getter or field defined.", name);
 				}
@@ -806,9 +896,9 @@ public class BeanPropertyMeta {
 				map.put(key, v);
 
 				if (setter != null)
-					setter.invoke(bean, map);
+					invokeSetter(bean, map);
 				else if (field != null)
-					field.set(bean, map);
+					invokeSetField(bean, map);
 				else
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to initialize map property ''{0}'', but no setter or field defined.", name);
 
@@ -816,9 +906,9 @@ public class BeanPropertyMeta {
 
 				Object b = null;
 				if (getter != null) {
-					b = getter.invoke(bean, (Object[])null);
+					b = invokeGetter(bean);
 				} else if (field != null) {
-					b = field.get(bean);
+					b = invokeGetField(bean);
 				} else {
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to append to bean property ''{0}'', but no getter or field defined.", name);
 				}
@@ -836,9 +926,9 @@ public class BeanPropertyMeta {
 				}
 
 				if (setter != null)
-					setter.invoke(bean, b);
+					invokeSetter(bean, b);
 				else if (field != null)
-					field.set(bean, b);
+					invokeSetField(bean, b);
 				else
 					throw new BeanRuntimeException(beanMeta.c, "Attempt to initialize bean property ''{0}'', but no setter or field defined.", name);
 			}