Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id CF4972009DC for ; Tue, 2 May 2017 15:58:48 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id CDEB8160BA1; Tue, 2 May 2017 13:58:48 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 9EF77160B9B for ; Tue, 2 May 2017 15:58:47 +0200 (CEST) Received: (qmail 8940 invoked by uid 500); 2 May 2017 13:58:46 -0000 Mailing-List: contact commits-help@juneau.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@juneau.incubator.apache.org Delivered-To: mailing list commits@juneau.incubator.apache.org Received: (qmail 8931 invoked by uid 99); 2 May 2017 13:58:46 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 02 May 2017 13:58:46 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 6FDF4CF6E1 for ; Tue, 2 May 2017 13:58:46 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.221 X-Spam-Level: X-Spam-Status: No, score=-4.221 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id yUjS5zIR_apX for ; Tue, 2 May 2017 13:58:44 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 065F85F5CA for ; Tue, 2 May 2017 13:58:42 +0000 (UTC) Received: (qmail 8921 invoked by uid 99); 2 May 2017 13:58:42 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 02 May 2017 13:58:42 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 1766ADFBC7; Tue, 2 May 2017 13:58:42 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jamesbognar@apache.org To: commits@juneau.incubator.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: incubator-juneau git commit: "extras" property, part 1. Date: Tue, 2 May 2017 13:58:42 +0000 (UTC) archived-at: Tue, 02 May 2017 13:58:49 -0000 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 Authored: Tue May 2 09:58:36 2017 -0400 Committer: JamesBognar 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 { // 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 { 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 { Map properties; Map getterProps = new HashMap(); Map setterProps = new HashMap(); + BeanPropertyMeta extrasProperty; + Map,Class[]> typeVarImpls; Constructor constructor; String[] constructorArgs = new String[0]; @@ -346,8 +350,12 @@ public class BeanMeta { if (dictionaryName == null) dictionaryName = findDictionaryName(this.classMeta); - for (Map.Entry e : normalProps.entrySet()) - properties.put(e.getKey(), e.getValue().build()); + for (Map.Entry 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. + *

+ * An extras property is one defined with @BeanProperty(name="*") + * + * @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 Object 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); }