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 */ 017package org.apache.commons.jxpath.ri.model.beans; 018 019import java.beans.IndexedPropertyDescriptor; 020import java.beans.PropertyDescriptor; 021 022import org.apache.commons.jxpath.JXPathBeanInfo; 023import org.apache.commons.jxpath.JXPathContext; 024import org.apache.commons.jxpath.JXPathInvalidAccessException; 025import org.apache.commons.jxpath.ri.model.NodePointer; 026import org.apache.commons.jxpath.util.ValueUtils; 027 028/** 029 * Pointer pointing to a property of a JavaBean. 030 * 031 * @author Dmitri Plotnikov 032 * @version $Revision: 1133499 $ $Date: 2011-06-08 20:19:50 +0200 (Mi, 08 Jun 2011) $ 033 */ 034public class BeanPropertyPointer extends PropertyPointer { 035 private static final long serialVersionUID = -6008991447676468786L; 036 037 private static final Object UNINITIALIZED = new Object(); 038 039 private String propertyName; 040 private JXPathBeanInfo beanInfo; 041 private Object baseValue = UNINITIALIZED; 042 private Object value = UNINITIALIZED; 043 private transient String[] names; 044 private transient PropertyDescriptor[] propertyDescriptors; 045 private transient PropertyDescriptor propertyDescriptor; 046 047 /** 048 * Create a new BeanPropertyPointer. 049 * @param parent parent pointer 050 * @param beanInfo describes the target property/ies. 051 */ 052 public BeanPropertyPointer(NodePointer parent, JXPathBeanInfo beanInfo) { 053 super(parent); 054 this.beanInfo = beanInfo; 055 } 056 057 /** 058 * This type of node is auxiliary. 059 * @return true 060 */ 061 public boolean isContainer() { 062 return true; 063 } 064 065 public int getPropertyCount() { 066 if (beanInfo.isAtomic()) { 067 return 0; 068 } 069 return getPropertyDescriptors().length; 070 } 071 072 /** 073 * Get the names of all properties, sorted alphabetically 074 * @return String[] 075 */ 076 public String[] getPropertyNames() { 077 if (names == null) { 078 PropertyDescriptor[] pds = getPropertyDescriptors(); 079 names = new String[pds.length]; 080 for (int i = 0; i < names.length; i++) { 081 names[i] = pds[i].getName(); 082 } 083 } 084 return names; 085 } 086 087 /** 088 * Select a property by name. 089 * @param propertyName String name 090 */ 091 public void setPropertyName(String propertyName) { 092 setPropertyIndex(UNSPECIFIED_PROPERTY); 093 this.propertyName = propertyName; 094 } 095 096 /** 097 * Selects a property by its offset in the alphabetically sorted list. 098 * @param index property index 099 */ 100 public void setPropertyIndex(int index) { 101 if (propertyIndex != index) { 102 super.setPropertyIndex(index); 103 propertyName = null; 104 propertyDescriptor = null; 105 baseValue = UNINITIALIZED; 106 value = UNINITIALIZED; 107 } 108 } 109 110 /** 111 * Get the value of the currently selected property. 112 * @return Object value 113 */ 114 public Object getBaseValue() { 115 if (baseValue == UNINITIALIZED) { 116 PropertyDescriptor pd = getPropertyDescriptor(); 117 if (pd == null) { 118 return null; 119 } 120 baseValue = ValueUtils.getValue(getBean(), pd); 121 } 122 return baseValue; 123 } 124 125 public void setIndex(int index) { 126 if (this.index == index) { 127 return; 128 } 129 // When dealing with a scalar, index == 0 is equivalent to 130 // WHOLE_COLLECTION, so do not change it. 131 if (this.index != WHOLE_COLLECTION 132 || index != 0 133 || isCollection()) { 134 super.setIndex(index); 135 value = UNINITIALIZED; 136 } 137 } 138 139 /** 140 * If index == WHOLE_COLLECTION, the value of the property, otherwise 141 * the value of the index'th element of the collection represented by the 142 * property. If the property is not a collection, index should be zero 143 * and the value will be the property itself. 144 * @return Object 145 */ 146 public Object getImmediateNode() { 147 if (value == UNINITIALIZED) { 148 if (index == WHOLE_COLLECTION) { 149 value = ValueUtils.getValue(getBaseValue()); 150 } 151 else { 152 PropertyDescriptor pd = getPropertyDescriptor(); 153 if (pd == null) { 154 value = null; 155 } 156 else { 157 value = ValueUtils.getValue(getBean(), pd, index); 158 } 159 } 160 } 161 return value; 162 } 163 164 protected boolean isActualProperty() { 165 return getPropertyDescriptor() != null; 166 } 167 168 public boolean isCollection() { 169 PropertyDescriptor pd = getPropertyDescriptor(); 170 if (pd == null) { 171 return false; 172 } 173 174 if (pd instanceof IndexedPropertyDescriptor) { 175 return true; 176 } 177 178 int hint = ValueUtils.getCollectionHint(pd.getPropertyType()); 179 if (hint == -1) { 180 return false; 181 } 182 if (hint == 1) { 183 return true; 184 } 185 186 Object value = getBaseValue(); 187 return value != null && ValueUtils.isCollection(value); 188 } 189 190 /** 191 * If the property contains a collection, then the length of that 192 * collection, otherwise - 1. 193 * @return int length 194 */ 195 public int getLength() { 196 PropertyDescriptor pd = getPropertyDescriptor(); 197 if (pd == null) { 198 return 1; 199 } 200 201 if (pd instanceof IndexedPropertyDescriptor) { 202 return ValueUtils.getIndexedPropertyLength( 203 getBean(), 204 (IndexedPropertyDescriptor) pd); 205 } 206 207 int hint = ValueUtils.getCollectionHint(pd.getPropertyType()); 208 if (hint == -1) { 209 return 1; 210 } 211 return super.getLength(); 212 } 213 214 /** 215 * If index == WHOLE_COLLECTION, change the value of the property, otherwise 216 * change the value of the index'th element of the collection 217 * represented by the property. 218 * @param value value to set 219 */ 220 public void setValue(Object value) { 221 PropertyDescriptor pd = getPropertyDescriptor(); 222 if (pd == null) { 223 throw new JXPathInvalidAccessException( 224 "Cannot set property: " + asPath() + " - no such property"); 225 } 226 227 if (index == WHOLE_COLLECTION) { 228 ValueUtils.setValue(getBean(), pd, value); 229 } 230 else { 231 ValueUtils.setValue(getBean(), pd, index, value); 232 } 233 this.value = value; 234 } 235 236 public NodePointer createPath(JXPathContext context) { 237 if (getImmediateNode() == null) { 238 super.createPath(context); 239 baseValue = UNINITIALIZED; 240 value = UNINITIALIZED; 241 } 242 return this; 243 } 244 245 public void remove() { 246 if (index == WHOLE_COLLECTION) { 247 setValue(null); 248 } 249 else if (isCollection()) { 250 Object o = getBaseValue(); 251 Object collection = ValueUtils.remove(getBaseValue(), index); 252 if (collection != o) { 253 ValueUtils.setValue(getBean(), getPropertyDescriptor(), collection); 254 } 255 } 256 else if (index == 0) { 257 index = WHOLE_COLLECTION; 258 setValue(null); 259 } 260 } 261 262 /** 263 * Get the name of the currently selected property. 264 * @return String property name 265 */ 266 public String getPropertyName() { 267 if (propertyName == null) { 268 PropertyDescriptor pd = getPropertyDescriptor(); 269 if (pd != null) { 270 propertyName = pd.getName(); 271 } 272 } 273 return propertyName != null ? propertyName : "*"; 274 } 275 276 /** 277 * Finds the property descriptor corresponding to the current property 278 * index. 279 * @return PropertyDescriptor 280 */ 281 private PropertyDescriptor getPropertyDescriptor() { 282 if (propertyDescriptor == null) { 283 int inx = getPropertyIndex(); 284 if (inx == UNSPECIFIED_PROPERTY) { 285 propertyDescriptor = 286 beanInfo.getPropertyDescriptor(propertyName); 287 } 288 else { 289 PropertyDescriptor[] propertyDescriptors = 290 getPropertyDescriptors(); 291 if (inx >= 0 && inx < propertyDescriptors.length) { 292 propertyDescriptor = propertyDescriptors[inx]; 293 } 294 else { 295 propertyDescriptor = null; 296 } 297 } 298 } 299 return propertyDescriptor; 300 } 301 302 /** 303 * Get all PropertyDescriptors. 304 * @return PropertyDescriptor[] 305 */ 306 protected synchronized PropertyDescriptor[] getPropertyDescriptors() { 307 if (propertyDescriptors == null) { 308 propertyDescriptors = beanInfo.getPropertyDescriptors(); 309 } 310 return propertyDescriptors; 311 } 312}