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 }