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.Proxy;
023: import java.util.HashMap;
024: import java.util.HashSet;
025: import java.util.Set;
026:
027: /**
028: * The BeanPropertyMap class represents a collection of property values where properties are
029: * stored in a local HashMap.
030: */
031: public class BeanPropertyMap extends BaseMap implements PropertyMap,
032: java.io.Serializable {
033: private static final HashMap _primToObject = new HashMap();
034:
035: static {
036: _primToObject.put(Integer.TYPE, Integer.class);
037: _primToObject.put(Long.TYPE, Long.class);
038: _primToObject.put(Short.TYPE, Short.class);
039: _primToObject.put(Byte.TYPE, Byte.class);
040: _primToObject.put(Float.TYPE, Float.class);
041: _primToObject.put(Double.TYPE, Double.class);
042: _primToObject.put(Character.TYPE, Character.class);
043: _primToObject.put(Boolean.TYPE, Boolean.class);
044: }
045:
046: /**
047: * Creates an empty BeanPropertyMap associated with the specific Control public
048: * interface, PropertySet, or annotation type.
049: */
050: public BeanPropertyMap(Class mapClass) {
051: setMapClass(mapClass);
052: }
053:
054: /**
055: * Creates a BeanPropertyMap that wraps another PropertyMap. Any changes via setProperty
056: * will be maintained locally on the constructed map, but getProperty will delegate to the
057: * base PropertyMap for properties not set locally.
058: */
059: public BeanPropertyMap(PropertyMap map) {
060: setMapClass(map.getMapClass());
061: setDelegateMap(map);
062: }
063:
064: /**
065: * Creates a BeanPropertyMap where default values are derived from a single annotation
066: * type instance. This can be used to create a map from a property getter return value,
067: * to modify element values.
068: */
069: public <T extends Annotation> BeanPropertyMap(T annot) {
070: // If the annotation value is actually a PropertySetProxy, then unwrap it and use
071: // the standard delegation model
072: try {
073: Object handler = Proxy.getInvocationHandler(annot);
074: if (handler instanceof PropertySetProxy) {
075: PropertySetProxy psp = (PropertySetProxy) handler;
076: setMapClass(psp.getPropertySet());
077: setDelegateMap(psp.getPropertyMap());
078: return;
079: }
080: } catch (IllegalArgumentException iae) {
081: } // regular annotation
082:
083: _annot = annot;
084: setMapClass(annot.getClass());
085: }
086:
087: /**
088: * Sets the property specifed by 'key' within this map.
089: */
090: public synchronized void setProperty(PropertyKey key, Object value) {
091: if (!isValidKey(key))
092: throw new IllegalArgumentException("Key " + key
093: + " is not valid for " + getMapClass());
094:
095: //
096: // Validate the value argument, based upon the property type reference by the key
097: //
098: Class propType = key.getPropertyType();
099: if (value == null) {
100: if (propType.isPrimitive() || propType.isAnnotation())
101: throw new IllegalArgumentException(
102: "Invalid null value for key " + key);
103: } else {
104: if (propType.isPrimitive())
105: propType = (Class) _primToObject.get(propType);
106:
107: if (!propType.isAssignableFrom(value.getClass())) {
108: throw new IllegalArgumentException("Value class ("
109: + value.getClass() + ") not of expected type: "
110: + propType);
111: }
112: }
113: _properties.put(key, value);
114: _propertySets.add(key.getPropertySet());
115: }
116:
117: /**
118: * Returns the property value specified by 'key' within this map.
119: */
120: public Object getProperty(PropertyKey key) {
121: if (!isValidKey(key))
122: throw new IllegalArgumentException("Key " + key
123: + " is not valid for " + getMapClass());
124:
125: //
126: // Check local properties first
127: //
128: if (_properties.containsKey(key))
129: return _properties.get(key);
130:
131: //
132: // Return the value of the annotation type instance (if any)
133: //
134: if (_annot != null)
135: return key.extractValue(_annot);
136:
137: //
138: // Call up to superclass, for delegation model / default value
139: //
140: return super .getProperty(key);
141: }
142:
143: /**
144: * Returns true if the PropertyMap contains one or more values for the specified
145: * PropertySet, false otherwise
146: */
147: public boolean containsPropertySet(
148: Class<? extends Annotation> propertySet) {
149: // If we have an annotation type instance and it matches up with the requested
150: // type, then return true
151: if (_annot != null && _annot.getClass().equals(propertySet))
152: return true;
153:
154: if (_propertySets.contains(propertySet))
155: return true;
156:
157: //
158: // Call up to superclass, for delegation model
159: //
160: return super .containsPropertySet(propertySet);
161: }
162:
163: /**
164: * Returns the set of PropertyKeys that are locally set in this property map. Note:
165: * this <b>does not</b> include any properties that might be set as a result of
166: * property lookup delegation.
167: */
168: public Set<PropertyKey> getPropertyKeys() {
169: return _properties.keySet();
170: }
171:
172: // local default annotation value, only set if annot constructor form is used
173: Annotation _annot;
174:
175: // locally maintained property values
176: HashMap<PropertyKey, Object> _properties = new HashMap<PropertyKey, Object>();
177:
178: // locally maintained PropertySets
179: HashSet<Class> _propertySets = new HashSet<Class>();
180: }
|