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:
023: import org.apache.beehive.controls.api.bean.ControlBean;
024: import org.apache.beehive.controls.api.bean.ControlExtension;
025: import org.apache.beehive.controls.api.bean.ControlInterface;
026: import org.apache.beehive.controls.api.bean.ExternalPropertySets;
027:
028: /**
029: * The BaseMap class provide an abstract base PropertyMap class from which other
030: * concrete PropertyMap implementation can derive. It contains some common code
031: * (such as property key validation and the implementation of the base delegation model)
032: * that is generically useful.
033: */
034: abstract public class BaseMap implements PropertyMap,
035: java.io.Serializable {
036: /**
037: * Sets the PropertySet or Control interface associated with this map. Only properties
038: * declared by the PropertySet or one of the PropertySets on the Control interface may
039: * be used with this map.
040: */
041: protected void setMapClass(Class mapClass) {
042: //
043: // If the provided map class is a ControlBean type, then locate associated control
044: // interface or extension that defines properties.
045: //
046: if (ControlBean.class.isAssignableFrom(mapClass)) {
047: Class[] intfs = mapClass.getInterfaces();
048: for (int i = 0; i < intfs.length; i++) {
049: if (intfs[i]
050: .isAnnotationPresent(ControlInterface.class)
051: || intfs[i]
052: .isAnnotationPresent(ControlExtension.class)) {
053: mapClass = intfs[i];
054: break;
055: }
056: }
057: } else {
058: if (!mapClass.isAnnotation()
059: && !mapClass
060: .isAnnotationPresent(ControlInterface.class)
061: && !mapClass
062: .isAnnotationPresent(ControlExtension.class))
063: throw new IllegalArgumentException(mapClass
064: + " must be Control or annotation type");
065: }
066:
067: _mapClass = mapClass;
068: }
069:
070: /**
071: * Returns the PropertySet or Control interface class associated with the PropertyMap.
072: */
073: public Class getMapClass() {
074: return _mapClass;
075: }
076:
077: /**
078: * Checks to see if the provided class is a control or property set interface that is
079: * compatible with the local PropertyMap.
080: */
081: private boolean isCompatibleClass(Class checkClass) {
082: //
083: // If the check class is equal to or a super-interface of the map class, then
084: // they are compatible.
085: //
086: if (_mapClass.isAssignableFrom(checkClass))
087: return true;
088:
089: //
090: // If the check class is a property set declared by the map class or a super interface
091: // of the map class, then they are compatible.
092: //
093: if (checkClass.isAnnotationPresent(PropertySet.class)) {
094: Class declaringClass = checkClass.getDeclaringClass();
095:
096: // External property sets are always compatible.
097: // TODO: Could do a more extensive check..
098: if (declaringClass == null)
099: return true;
100:
101: if (declaringClass.isAssignableFrom(_mapClass))
102: return true;
103: }
104:
105: //
106: // If the map class is a property set declared by the check class or a super interface
107: // of the check class, then they are compatible. This is the inverse of the last check,
108: // and happens e.g. when a programatically instantiated control w/ an initial property
109: // map needs to delegate to the control interface's property map.
110: //
111: if (_mapClass.isAnnotationPresent(PropertySet.class)) {
112: Class declaringClass = _mapClass.getDeclaringClass();
113: if (declaringClass != null
114: && declaringClass.isAssignableFrom(checkClass))
115: return true;
116:
117: // External property sets have no declaring class
118: if (declaringClass == null) {
119: ExternalPropertySets eps = (ExternalPropertySets) checkClass
120: .getAnnotation(ExternalPropertySets.class);
121: if (eps != null) {
122: Class[] propSets = eps.value();
123: if (propSets != null) {
124: for (Class ps : propSets) {
125: if (_mapClass.equals(ps))
126: return true;
127: }
128: }
129: }
130: }
131: }
132:
133: return false;
134: }
135:
136: /**
137: * Checks to ensure that the provided key is a valid key for this PropertyMap
138: */
139: protected boolean isValidKey(PropertyKey key) {
140: return isCompatibleClass(key.getPropertySet());
141: }
142:
143: /**
144: * Sets a delegate base property map from which values will be derived if not found within
145: * the local property map.
146: */
147: public synchronized void setDelegateMap(PropertyMap delegateMap) {
148: if (!isCompatibleClass(delegateMap.getMapClass()))
149: throw new IllegalArgumentException(
150: "The delegate map type ("
151: + delegateMap.getMapClass()
152: + " is an incompatible type with "
153: + _mapClass);
154:
155: _delegateMap = delegateMap;
156: }
157:
158: /**
159: * Returns a delegate base property map from which values will be derived if not found within
160: * the local property map.
161: */
162: public PropertyMap getDelegateMap() {
163: return _delegateMap;
164: }
165:
166: /**
167: * Returns the property value specified by 'key' within this map.
168: */
169: public Object getProperty(PropertyKey key) {
170: //
171: // Delegate up to any parent map
172: //
173: if (_delegateMap != null)
174: return _delegateMap.getProperty(key);
175:
176: //
177: // If neither found a value, return the default value
178: //
179: return key.getDefaultValue();
180: }
181:
182: /**
183: * Returns true if the PropertyMap contains one or more values for the specified
184: * PropertySet, false otherwise.
185: */
186: public boolean containsPropertySet(
187: Class<? extends Annotation> propertySet) {
188: //
189: // Defer to any delegate map
190: //
191: if (_delegateMap != null)
192: return _delegateMap.containsPropertySet(propertySet);
193:
194: return false;
195: }
196:
197: /**
198: * Returns a PropertySet proxy instance that derives its data from the contents of
199: * the property map. Will return null if the PropertyMap does not contain any properties
200: * associated with the specified PropertySet.
201: */
202: public <T extends Annotation> T getPropertySet(Class<T> propertySet) {
203: if (!containsPropertySet(propertySet))
204: return null;
205:
206: return PropertySetProxy.getProxy(propertySet, this );
207: }
208:
209: Class _mapClass; // associated Control or PropertySet class
210: PropertyMap _delegateMap; // wrapped PropertyMap (or null)
211: }
|