1 package org.apache.commons.ognl;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.beans.IntrospectionException;
23 import java.beans.PropertyDescriptor;
24 import java.lang.reflect.Method;
25
26 /**
27 * <p>
28 * PropertyDescriptor subclass that describes an indexed set of read/write methods to get a property. Unlike
29 * IndexedPropertyDescriptor this allows the "key" to be an arbitrary object rather than just an int. Consequently it
30 * does not have a "readMethod" or "writeMethod" because it only expects a pattern like:
31 * </p>
32 *
33 * <pre>
34 * public void set<i>Property</i>(<i>KeyType</i>, <i>ValueType</i>);
35 * public <i>ValueType</i> get<i>Property</i>(<i>KeyType</i>);
36 * </pre>
37 * <p>
38 * and does not require the methods that access it as an array. OGNL can get away with this without losing functionality
39 * because if the object does expose the properties they are most probably in a Map and that case is handled by the
40 * normal OGNL property accessors.
41 * </p>
42 * <p>
43 * For example, if an object were to have methods that accessed and "attributes" property it would be natural to index
44 * them by String rather than by integer and expose the attributes as a map with a different property name:
45 *
46 * <pre>
47 * public void setAttribute( String name, Object value );
48 *
49 * public Object getAttribute( String name );
50 *
51 * public Map getAttributes();
52 * </pre>
53 * <p>
54 * Note that the index get/set is called get/set <code>Attribute</code> whereas the collection getter is called
55 * <code>Attributes</code>. This case is handled unambiguously by the OGNL property accessors because the set/get
56 * <code>Attribute</code> methods are detected by this object and the "attributes" case is handled by the
57 * <code>MapPropertyAccessor</code>. Therefore OGNL expressions calling this code would be handled in the following way:
58 * </p>
59 * <table>
60 * <tr>
61 * <th>OGNL Expression</th>
62 * <th>Handling</th>
63 * </tr>
64 * <tr>
65 * <td><code>attribute["name"]</code></td>
66 * <td>Handled by an index getter, like <code>getAttribute(String)</code>.</td>
67 * </tr>
68 * <tr>
69 * <td><code>attribute["name"] = value</code></td>
70 * <td>Handled by an index setter, like <code>setAttribute(String, Object)</code>.</td>
71 * </tr>
72 * <tr>
73 * <td><code>attributes["name"]</code></td>
74 * <td>Handled by <code>MapPropertyAccessor</code> via a <code>Map.get()</code>. This will <b>not</b> go through the
75 * index get accessor.</td>
76 * </tr>
77 * <tr>
78 * <td><code>attributes["name"] = value</code></td>
79 * <td>Handled by <code>MapPropertyAccessor</code> via a <code>Map.put()</code>. This will <b>not</b> go through the
80 * index set accessor.</td>
81 * </tr>
82 * </table>
83 *
84 * @author Luke Blanshard (blanshlu@netscape.net)
85 * @author Drew Davidson (drew@ognl.org)
86 */
87 public class ObjectIndexedPropertyDescriptor
88 extends PropertyDescriptor
89 {
90 private Method indexedReadMethod;
91
92 private Method indexedWriteMethod;
93
94 private Class<?> propertyType;
95
96 public ObjectIndexedPropertyDescriptor( String propertyName, Class<?> propertyType, Method indexedReadMethod,
97 Method indexedWriteMethod )
98 throws IntrospectionException
99 {
100 super( propertyName, null, null );
101 this.propertyType = propertyType;
102 this.indexedReadMethod = indexedReadMethod;
103 this.indexedWriteMethod = indexedWriteMethod;
104 }
105
106 public Method getIndexedReadMethod()
107 {
108 return indexedReadMethod;
109 }
110
111 public Method getIndexedWriteMethod()
112 {
113 return indexedWriteMethod;
114 }
115
116 @Override
117 public Class<?> getPropertyType()
118 {
119 return propertyType;
120 }
121
122 @Override
123 public boolean equals(Object o) {
124 if (this == o)
125 {
126 return true;
127 }
128
129 if (!(o instanceof ObjectIndexedPropertyDescriptor))
130 {
131 return false;
132 }
133
134 if (!super.equals(o))
135 {
136 return false;
137 }
138
139 ObjectIndexedPropertyDescriptor that = (ObjectIndexedPropertyDescriptor) o;
140
141 if (indexedReadMethod != null ? !indexedReadMethod.equals(that.indexedReadMethod) : that.indexedReadMethod != null)
142 {
143 return false;
144 }
145
146 if (indexedWriteMethod != null ? !indexedWriteMethod.equals(that.indexedWriteMethod) : that.indexedWriteMethod != null)
147 {
148 return false;
149 }
150
151 if (propertyType != null ? !propertyType.equals(that.propertyType) : that.propertyType != null)
152 {
153 return false;
154 }
155
156 return true;
157 }
158
159 @Override
160 public int hashCode() {
161 int result = super.hashCode();
162 result = 31 * result + (indexedReadMethod != null ? indexedReadMethod.hashCode() : 0);
163 result = 31 * result + (indexedWriteMethod != null ? indexedWriteMethod.hashCode() : 0);
164 result = 31 * result + (propertyType != null ? propertyType.hashCode() : 0);
165 return result;
166 }
167 }