001 package org.apache.commons.ognl; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import java.beans.IntrospectionException; 023 import java.beans.PropertyDescriptor; 024 import java.lang.reflect.Method; 025 026 /** 027 * <p> 028 * PropertyDescriptor subclass that describes an indexed set of read/write methods to get a property. Unlike 029 * IndexedPropertyDescriptor this allows the "key" to be an arbitrary object rather than just an int. Consequently it 030 * does not have a "readMethod" or "writeMethod" because it only expects a pattern like: 031 * </p> 032 * 033 * <pre> 034 * public void set<i>Property</i>(<i>KeyType</i>, <i>ValueType</i>); 035 * public <i>ValueType</i> get<i>Property</i>(<i>KeyType</i>); 036 * </pre> 037 * <p> 038 * and does not require the methods that access it as an array. OGNL can get away with this without losing functionality 039 * because if the object does expose the properties they are most probably in a Map and that case is handled by the 040 * normal OGNL property accessors. 041 * </p> 042 * <p> 043 * For example, if an object were to have methods that accessed and "attributes" property it would be natural to index 044 * them by String rather than by integer and expose the attributes as a map with a different property name: 045 * 046 * <pre> 047 * public void setAttribute( String name, Object value ); 048 * 049 * public Object getAttribute( String name ); 050 * 051 * public Map getAttributes(); 052 * </pre> 053 * <p> 054 * Note that the index get/set is called get/set <code>Attribute</code> whereas the collection getter is called 055 * <code>Attributes</code>. This case is handled unambiguously by the OGNL property accessors because the set/get 056 * <code>Attribute</code> methods are detected by this object and the "attributes" case is handled by the 057 * <code>MapPropertyAccessor</code>. Therefore OGNL expressions calling this code would be handled in the following way: 058 * </p> 059 * <table> 060 * <tr> 061 * <th>OGNL Expression</th> 062 * <th>Handling</th> 063 * </tr> 064 * <tr> 065 * <td><code>attribute["name"]</code></td> 066 * <td>Handled by an index getter, like <code>getAttribute(String)</code>.</td> 067 * </tr> 068 * <tr> 069 * <td><code>attribute["name"] = value</code></td> 070 * <td>Handled by an index setter, like <code>setAttribute(String, Object)</code>.</td> 071 * </tr> 072 * <tr> 073 * <td><code>attributes["name"]</code></td> 074 * <td>Handled by <code>MapPropertyAccessor</code> via a <code>Map.get()</code>. This will <b>not</b> go through the 075 * index get accessor.</td> 076 * </tr> 077 * <tr> 078 * <td><code>attributes["name"] = value</code></td> 079 * <td>Handled by <code>MapPropertyAccessor</code> via a <code>Map.put()</code>. This will <b>not</b> go through the 080 * index set accessor.</td> 081 * </tr> 082 * </table> 083 * 084 * @author Luke Blanshard (blanshlu@netscape.net) 085 * @author Drew Davidson (drew@ognl.org) 086 */ 087 public class ObjectIndexedPropertyDescriptor 088 extends PropertyDescriptor 089 { 090 private Method indexedReadMethod; 091 092 private Method indexedWriteMethod; 093 094 private Class<?> propertyType; 095 096 public ObjectIndexedPropertyDescriptor( String propertyName, Class<?> propertyType, Method indexedReadMethod, 097 Method indexedWriteMethod ) 098 throws IntrospectionException 099 { 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 }