001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.controls.api.properties;
020:
021: import java.lang.annotation.Annotation;
022: import java.lang.reflect.InvocationHandler;
023: import java.lang.reflect.Method;
024: import java.lang.reflect.Proxy;
025:
026: /**
027: * The PropertySetProxy class is a dynamic proxy {@link InvocationHandler} class that exposes the
028: * values held within a PropertyMap as an Object implementing an annotation type interface.
029: * <p>
030: * This enables properties resolved using the {@link PropertyMap}'s hiearchical resolution mechanism to
031: * be exposed to the client of the proxy in the same way that Java 5 annotations are
032: * exposed using raw Java reflection APIs. A proxy of this type should behave identically
033: * to the one returned from a call to <code>AnnotatedElement.getAnnotation()</code>, but backed
034: * by a richer, more dynamic resolution mechanism.
035: *
036: * @see java.lang.reflect.Proxy
037: * @see java.lang.reflect.InvocationHandler
038: * @see java.lang.reflect.AnnotatedElement#getAnnotation
039: * @see org.apache.beehive.controls.api.properties.PropertySet
040: * @see org.apache.beehive.controls.api.properties.PropertyMap
041: */
042: public class PropertySetProxy<T extends Annotation> implements
043: InvocationHandler {
044: /**
045: * Creates a new proxy instance implementing the PropertySet interface and backed
046: * by the data from the property map.
047: *
048: * @param propertySet an annotation type that has the PropertySet meta-annotation
049: * @param propertyMap the PropertyMap containing property values backing the proxy
050: * @return proxy that implements the PropertySet interface
051: */
052: public static <T extends Annotation> T getProxy(
053: Class<T> propertySet, PropertyMap propertyMap) {
054: assert propertySet != null && propertyMap != null;
055:
056: if (!propertySet.isAnnotation())
057: throw new IllegalArgumentException(propertySet
058: + " is not an annotation type");
059:
060: return (T) Proxy.newProxyInstance(propertySet.getClassLoader(),
061: new Class[] { propertySet }, new PropertySetProxy(
062: propertySet, propertyMap));
063: }
064:
065: /**
066: * Private constructor, called only from the getProxy factory method
067: */
068: private PropertySetProxy(Class<T> propertySet,
069: PropertyMap propertyMap) {
070: _propertySet = propertySet;
071: _propertyMap = propertyMap;
072: }
073:
074: //
075: // InvocationHandler.invoke
076: //
077: public Object invoke(Object proxy, Method method, Object[] args)
078: throws Throwable {
079: // Handle cases where Object/Annotation methods are called on this
080: // proxy. We were getting null back from Annotation.annotationType.
081: Object value = null;
082: if (method.getDeclaringClass() == Object.class) {
083: try {
084: if (method.getName().equals("getClass")) {
085: value = _propertySet;
086: } else {
087: value = method.invoke(_propertyMap, args);
088: }
089: } catch (Exception e) {
090: e.printStackTrace();
091: }
092: } else if (method.getDeclaringClass() == Annotation.class
093: && method.getName().equals("annotationType")) {
094: value = _propertySet;
095: } else {
096:
097: // Query the nested value in the property map
098: PropertyKey key = new PropertyKey(_propertySet, method
099: .getName());
100: value = _propertyMap.getProperty(key);
101:
102: // If the returned value is itself a PropertyMap (i.e. a nested annotation type),
103: // then wrap it in a PropertySetProxy instance before returning.
104: if (value instanceof PropertyMap) {
105: PropertyMap propertyMap = (PropertyMap) value;
106: value = getProxy(propertyMap.getMapClass(), propertyMap);
107: }
108: }
109:
110: return value;
111: }
112:
113: /**
114: * Returns the PropertySet annotation type associated with the proxy
115: */
116: public Class<T> getPropertySet() {
117: return _propertySet;
118: }
119:
120: /**
121: * Returns the underlying PropertyMap containing the property values exposed by the
122: * proxy.
123: */
124: public PropertyMap getPropertyMap() {
125: return _propertyMap;
126: }
127:
128: private Class<T> _propertySet;
129: private PropertyMap _propertyMap;
130: }
|