Author: kylem Date: Mon Jan 3 16:51:12 2005 New Revision: 124048 URL: http://svn.apache.org/viewcvs?view=rev&rev=124048 Log: Updated handling of annotation type-valued properties to resolve BEEHIVE-149. The setter method now takes an instance of the annotation type, and the PropertyMap interface exposes a helper method (getPropertySet) that returns an annotation type proxy instance based upon map contents. There is also a new constructor for BeanPropertyMap class that takes an annotation type instance, to fully enable get property -> create property map -> update property map -> set property use case.
Added: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertySetProxy.java Removed: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/PropertySetProxy.java Modified: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java Modified: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java&r1=124047&p2=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java (original) +++ incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BaseMap.java Mon Jan 3 16:51:12 2005 @@ -170,6 +170,19 @@ return false; } + /** + * Returns a PropertySet proxy instance that derives its data from the contents of + * the property map. Will return null if the PropertyMap does not contain any properties + * associated with the specified PropertySet. + */ + public <T extends Annotation> T getPropertySet(Class<T> propertySet) + { + if (!containsPropertySet(propertySet)) + return null; + + return PropertySetProxy.getProxy(propertySet, this); + } + Class _mapClass; // associated Control or PropertySet class PropertyMap _delegateMap; // wrapped PropertyMap (or null) } Modified: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java&r1=124047&p2=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java (original) +++ incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/BeanPropertyMap.java Mon Jan 3 16:51:12 2005 @@ -21,6 +21,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -71,6 +72,32 @@ } /** + * Creates a BeanPropertyMap where default values are derived from a single annotation + * type instance. This can be used to create a map from a property getter return value, + * to modify element values. + */ + public <T extends Annotation> BeanPropertyMap(T annot) + { + // If the annotation value is actually a PropertySetProxy, then unwrap it and use + // the standard delegation model + try + { + Object handler = Proxy.getInvocationHandler(annot); + if (handler instanceof PropertySetProxy) + { + PropertySetProxy psp = (PropertySetProxy)handler; + setMapClass(psp.getPropertySet()); + setDelegateMap(psp.getPropertyMap()); + return; + } + } + catch (IllegalArgumentException iae) {} // regular annotation + + _annot = annot; + setMapClass(annot.getClass()); + } + + /** * Sets the property specifed by 'key' within this map. */ public void setProperty(PropertyKey key, Object value) @@ -91,8 +118,6 @@ { if (propType.isPrimitive()) propType = (Class)_primToObject.get(propType); - else if (propType.isAnnotation()) - propType = PropertyMap.class; if (!propType.isAssignableFrom(value.getClass())) { @@ -119,6 +144,12 @@ return _properties.get(key); // + // Return the value of the annotation type instance (if any) + // + if (_annot != null) + return key.extractValue(_annot); + + // // Call up to superclass, for delegation model / default value // return super.getProperty(key); @@ -130,6 +161,11 @@ */ public boolean containsPropertySet(Class<? extends Annotation> propertySet) { + // If we have an annotation type instance and it matches up with the requested + // type, then return true + if (_annot != null && _annot.getClass().equals(propertySet)) + return true; + if (_propertySets.contains(propertySet)) return true; @@ -138,6 +174,9 @@ // return super.containsPropertySet(propertySet); } + + // local default annotation value, only set if annot constructor form is used + Annotation _annot; // locally maintained property values HashMap<PropertyKey,Object> _properties = new HashMap<PropertyKey,Object>(); Modified: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java&r1=124047&p2=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java (original) +++ incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyMap.java Mon Jan 3 16:51:12 2005 @@ -63,4 +63,11 @@ * PropertySet, false otherwise */ public boolean containsPropertySet(Class<? extends Annotation> propertySet); + + /** + * Returns a PropertySet proxy instance that derives its data from the contents of + * the property map. Will return null if the PropertyMap does not contain any properties + * associated with the specified PropertySet. + */ + public <T extends Annotation> T getPropertySet(Class<T> propertySet); } Added: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertySetProxy.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertySetProxy.java?view=auto&rev=124048 ============================================================================== --- (empty file) +++ incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertySetProxy.java Mon Jan 3 16:51:12 2005 @@ -0,0 +1,138 @@ +package org.apache.beehive.controls.api.properties; +/* + * Copyright 2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Header:$ + */ + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.apache.beehive.controls.api.properties.PropertyKey; +import org.apache.beehive.controls.api.properties.PropertyMap; +import org.apache.beehive.controls.api.properties.PropertySet; + +/** + * The PropertySetProxy class is a dynamic proxy InvocationHandler class that exposes the + * values held within a PropertyMap as an Object implementing an annotation type interface. + * <p> + * This enables properties resolved using the PropertyMap's hiearchical resolution mechanism to + * be exposed to the client of the proxy in the same way that JSR-175 annotations are + * exposed using raw Java reflection APIs. A proxy of this type should behave identically + * to the one returned from a call to <code>AnnotatedElement.getAnnotation()</code>, but backed + * by a richer, more dynamic resolution mechanism. + * + * @see java.lang.reflect.Proxy + * @see java.lang.reflect.InvocationHandler + * @see java.lang.reflect.AnnotatedElement#getAnnotation + * @see org.apache.beehive.controls.api.properties.PropertySet + * @see org.apache.beehive.controls.api.properties.PropertyMap + */ +public class PropertySetProxy <T extends Annotation> implements InvocationHandler +{ + /** + * Creates a new proxy instance implementing the PropertySet interface and backed + * by the data from the property map. + * + * @param propertySet an annotation type that has the PropertySet meta-annotation + * @param propertyMap the PropertyMap containing property values backing the proxy + * @return proxy that implements the PropertySet interface + */ + public static <T extends Annotation> T getProxy(Class<T> propertySet, PropertyMap propertyMap) + { + assert propertySet != null && propertyMap != null; + + if (!propertySet.isAnnotation()) + throw new IllegalArgumentException(propertySet + " is not an annotation type"); + + return (T) Proxy.newProxyInstance(propertySet.getClassLoader(), + new Class [] {propertySet }, + new PropertySetProxy(propertySet, propertyMap)); + } + + /** + * Private constructor, called only from the getProxy factory method + */ + private PropertySetProxy(Class<T> propertySet, PropertyMap propertyMap) + { + _propertySet = propertySet; + _propertyMap = propertyMap; + } + + // + // InvocationHandler.invoke + // + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + // Handle cases where Object/Annotation methods are called on this + // proxy. We were getting null back from Annotation.annotationType. + Object value = null; + if (method.getDeclaringClass() == Object.class) + { + try { + if (method.getName().equals("getClass")) + { + value = _propertySet; + } + else + { + value = method.invoke(_propertyMap, args); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + else if (method.getDeclaringClass() == Annotation.class && + method.getName().equals("annotationType")) + { + value = _propertySet; + } + else + { + + // Query the nested value in the property map + PropertyKey key = new PropertyKey(_propertySet, method.getName()); + value = _propertyMap.getProperty(key); + + // If the returned value is itself a PropertyMap (i.e. a nested annotation type), + // then wrap it in a PropertySetProxy instance before returning. + if (value instanceof PropertyMap) + { + PropertyMap propertyMap = (PropertyMap)value; + value = getProxy(propertyMap.getMapClass(), propertyMap); + } + } + + return value; + } + + /** + * Returns the PropertySet annotation type associated with the proxy + */ + public Class<T> getPropertySet() { return _propertySet; } + + /** + * Returns the underlying PropertyMap containing the property values exposed by the + * proxy. + */ + public PropertyMap getPropertyMap() { return _propertyMap; } + + private Class<T> _propertySet; + private PropertyMap _propertyMap; +} Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java&r1=124047&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java Mon Jan 3 16:51:12 2005 @@ -43,6 +43,7 @@ import org.apache.beehive.controls.api.properties.BeanPropertyMap; import org.apache.beehive.controls.api.properties.PropertyKey; import org.apache.beehive.controls.api.properties.PropertyMap; +import org.apache.beehive.controls.api.properties.PropertySetProxy; import org.apache.beehive.controls.spi.svc.Interceptor; /** Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java&r1=124047&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java Mon Jan 3 16:51:12 2005 @@ -39,8 +39,9 @@ import org.apache.beehive.controls.api.bean.ControlInterface; import org.apache.beehive.controls.api.context.ControlHandle; import org.apache.beehive.controls.api.properties.AnnotatedElementMap; -import org.apache.beehive.controls.api.properties.PropertyMap; import org.apache.beehive.controls.api.properties.BeanPropertyMap; +import org.apache.beehive.controls.api.properties.PropertyMap; +import org.apache.beehive.controls.api.properties.PropertySetProxy; /** * The ControlBeanContext implements the basic BeanContextServices implementation Deleted: /incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/PropertySetProxy.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/PropertySetProxy.java?view=auto&rev=124047 ============================================================================== Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java&r1=124047&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java Mon Jan 3 16:51:12 2005 @@ -55,7 +55,7 @@ setDeclaration(eventSet); EventSet eventSetAnnot = eventSet.getAnnotation(EventSet.class); - if (eventSet != null) + if (eventSetAnnot != null) _unicast = eventSetAnnot.unicast(); // Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java&r1=124047&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptProperty.java Mon Jan 3 16:51:12 2005 @@ -84,6 +84,28 @@ } /** + * Returns the name of the property reading accessor method + */ + public String getReadMethod() + { + StringBuffer sb = new StringBuffer(); + if (getType().equals("boolean")) + sb.append("is"); + else + sb.append("get"); + sb.append(getAccessorName()); + return sb.toString(); + } + + /** + * Returns the name of the property writing accessor method + */ + public String getWriteMethod() + { + return "set" + getAccessorName(); + } + + /** * Returns the name associated with this Property in the PropertySet */ public String getName() Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm&r1=124047&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm Mon Jan 3 16:51:12 2005 @@ -177,13 +177,13 @@ ## This macro defines the property accessor methods for a bean property ## #macro (declarePropertyAccessors $property) + /** + * A PropertyKey that can be used to access the $property.name property of the + * $property.propertySet.shortName PropertySet + */ public static PropertyKey $property.keyName = new PropertyKey(${property.propertySet.className}.class, "$property.name"); - #if ($property.isAnnotation()) - public void set${property.accessorName}(PropertyMap value) - #else - public void set${property.accessorName}($property.type value) - #end + public void ${property.writeMethod}($property.type value) #if ($property.constrained) throws java.beans.PropertyVetoException #end @@ -204,11 +204,7 @@ #end } - #if ($property.getType().equals("boolean")) - public $property.type is${property.accessorName}() - #else - public $property.type get${property.accessorName}() - #end + public $property.type ${property.readMethod}() { return (#toObject($property.type))getControlProperty($property.keyName); } Modified: incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java?view=diff&rev=124048&p1=incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java&r1=124047&p2=incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java&r2=124048 ============================================================================== --- incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java (original) +++ incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropsBeans.java Mon Jan 3 16:51:12 2005 @@ -337,7 +337,7 @@ simpleAnnotMap.setProperty(new PropertyKey(SimpleProps.class, "simpleClass"), testAnnotClass); - propBean.setSimpleAnnot(simpleAnnotMap); + propBean.setSimpleAnnot(simpleAnnotMap.getPropertySet(SimpleProps.class)); simpleAnnot = propBean.getSimpleAnnot(); if (simpleAnnot == null){
