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.Method;
023:
024: import org.apache.beehive.controls.api.ControlException;
025:
026: /**
027: * The PropertyKey class represents a key that can be used to set a JSR-175 attribute member
028: * value within a <code>PropertyMap</code>.
029: */
030: public class PropertyKey implements java.io.Serializable {
031: /**
032: * This constructor takes the JSR-175 metadata interface that is associated with
033: * the contained attributes.
034: */
035: public PropertyKey(Class<? extends Annotation> propertySet,
036: String propertyName) {
037: if (!propertySet.isAnnotation()) {
038: throw new IllegalArgumentException("Class " + propertySet
039: + " is not a valid annotation type");
040: }
041:
042: try {
043: _getMethod = propertySet.getMethod(propertyName,
044: (Class[]) null);
045: _propertySet = propertySet;
046: _propertyName = propertyName;
047: _propertyType = _getMethod.getReturnType();
048:
049: //
050: // Compute a hash code for the key instance that will be constant for all keys
051: // that reference the same interface/member combo
052: //
053: _hashCode = new String(propertySet.getName() + "."
054: + propertyName).hashCode();
055: } catch (NoSuchMethodException nsme) {
056: throw new IllegalArgumentException(
057: propertyName
058: + "is not a valid member of the metadata interface "
059: + propertySet);
060: }
061: }
062:
063: protected Method getMethod() {
064: if (null == _getMethod) {
065: try {
066: _getMethod = _propertySet.getMethod(_propertyName,
067: (Class[]) null);
068: } catch (NoSuchMethodException nsmEx) {
069: // This can only happen if a PropertySet is incompatibly changed after
070: // serialization of a PropertyKey (since it is initially validated in
071: // the constructor).
072: throw new ControlException(
073: "Unable to locate PropertyKey accessor method",
074: nsmEx);
075: }
076: }
077: return _getMethod;
078: }
079:
080: /**
081: * Computes the default value for the value of this property key, or null if there
082: * is no defined default.
083: */
084: public Object getDefaultValue() {
085: // Query the accessor method for the default value
086: // This method will return 'null' if there is no defined default
087: return getMethod().getDefaultValue();
088: }
089:
090: /**
091: * Extracts the value of the key from an Annotation instance
092: */
093: /* package */Object extractValue(Annotation annot) {
094: try {
095: return getMethod().invoke(annot, new Object[] {});
096: }
097: // TODO -- cleanup exception handling, property defining a PropertyException
098: catch (RuntimeException re) {
099: throw re;
100: } catch (Exception e) {
101: throw new RuntimeException("Unable to extract value for "
102: + _propertyName, e);
103: }
104: }
105:
106: public boolean equals(Object obj) {
107: // fast success for static key declaration cases
108: if (this == obj)
109: return true;
110:
111: // fast fail on obvious differences
112: if (obj == null || !(obj instanceof PropertyKey)
113: || _hashCode != obj.hashCode())
114: return false;
115:
116: // slower success on two equivalent keys constructed independently
117: PropertyKey keyObj = (PropertyKey) obj;
118: return _propertySet.equals(keyObj._propertySet)
119: && _propertyName.equals(keyObj._propertyName);
120: }
121:
122: public int hashCode() {
123: return _hashCode;
124: }
125:
126: public String toString() {
127: return "PropertyKey: " + _propertySet.getName() + "."
128: + _propertyName;
129: }
130:
131: public Class<? extends Annotation> getPropertySet() {
132: return _propertySet;
133: }
134:
135: public String getPropertyName() {
136: return _propertyName;
137: }
138:
139: public Class getPropertyType() {
140: return _propertyType;
141: }
142:
143: public Annotation[] getAnnotations() {
144: return getMethod().getAnnotations();
145: }
146:
147: Class<? extends Annotation> _propertySet;
148: String _propertyName;
149: Class _propertyType;
150: int _hashCode;
151:
152: // WARNING: This field should never be accessed directly but instead via the getMethod()
153: // API. This ensures that the (transient) value is appropriately recomputed when necessary.
154: private transient Method _getMethod;
155: }
|