View Javadoc

1   /*
2    * Copyright 1999-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jxpath.ri.model.beans;
17  
18  import java.beans.IndexedPropertyDescriptor;
19  import java.beans.PropertyDescriptor;
20  
21  import org.apache.commons.jxpath.JXPathBeanInfo;
22  import org.apache.commons.jxpath.JXPathContext;
23  import org.apache.commons.jxpath.JXPathException;
24  import org.apache.commons.jxpath.ri.model.NodePointer;
25  import org.apache.commons.jxpath.util.ValueUtils;
26  
27  /***
28   * Pointer pointing to a property of a JavaBean.
29   *
30   * @author Dmitri Plotnikov
31   * @version $Revision: 1.17 $ $Date: 2004/04/04 22:06:36 $
32   */
33  public class BeanPropertyPointer extends PropertyPointer {
34      private String propertyName;
35      private JXPathBeanInfo beanInfo;
36      private PropertyDescriptor propertyDescriptors[];
37      private PropertyDescriptor propertyDescriptor;
38      private String[] names;
39      private static final Object UNINITIALIZED = new Object();
40      private Object baseValue = UNINITIALIZED;
41      private Object value = UNINITIALIZED;
42      
43      private static final int UNKNOWN_LENGTH_MAX_COUNT = 10000;
44  
45      public BeanPropertyPointer(NodePointer parent, JXPathBeanInfo beanInfo) {
46          super(parent);
47          this.beanInfo = beanInfo;
48      }
49  
50      /***
51       * This type of node is auxiliary.
52       */
53      public boolean isContainer() {
54          return true;
55      }
56  
57      /***
58       * Number of the bean's properties.
59       */
60      public int getPropertyCount() {
61          return getPropertyDescriptors().length;
62      }
63  
64      /***
65       * Names of all properties, sorted alphabetically
66       */
67      public String[] getPropertyNames() {
68          if (names == null) {
69              PropertyDescriptor pds[] = getPropertyDescriptors();
70              names = new String[pds.length];
71              for (int i = 0; i < names.length; i++) {
72                  names[i] = pds[i].getName();
73              }
74          }
75          return names;
76      }
77  
78      /***
79       * Select a property by name
80       */
81      public void setPropertyName(String propertyName) {
82          setPropertyIndex(UNSPECIFIED_PROPERTY);
83          this.propertyName = propertyName;
84      }
85  
86      /***
87       * Selects a property by its offset in the alphabetically sorted list.
88       */
89      public void setPropertyIndex(int index) {
90          if (propertyIndex != index) {
91              super.setPropertyIndex(index);
92              propertyName = null;
93              propertyDescriptor = null;
94              baseValue = UNINITIALIZED;
95              value = UNINITIALIZED;
96          }
97      }
98  
99      /***
100      * The value of the currently selected property.
101      */
102     public Object getBaseValue() {
103         if (baseValue == UNINITIALIZED) {
104             PropertyDescriptor pd = getPropertyDescriptor();
105             if (pd == null) {
106                 return null;
107             }
108             baseValue = ValueUtils.getValue(getBean(), pd);
109         }
110         return baseValue;
111     }
112 
113     public void setIndex(int index) {
114         if (this.index != index) {
115             // When dealing with a scalar, index == 0 is equivalent to
116             // WHOLE_COLLECTION, so do not change it.
117             if (this.index != WHOLE_COLLECTION
118                 || index != 0
119                 || isCollection()) {
120                 super.setIndex(index);
121                 value = UNINITIALIZED;
122             }
123         }
124     }
125 
126     /***
127      * If index == WHOLE_COLLECTION, the value of the property, otherwise
128      * the value of the index'th element of the collection represented by the
129      * property. If the property is not a collection, index should be zero
130      * and the value will be the property itself.
131      */
132     public Object getImmediateNode() {
133         if (value == UNINITIALIZED) {
134             if (index == WHOLE_COLLECTION) {
135                 value = ValueUtils.getValue(getBaseValue());
136             }
137             else {
138                 PropertyDescriptor pd = getPropertyDescriptor();
139                 if (pd == null) {
140                     value = null;
141                 }
142                 else {
143                     value = ValueUtils.getValue(getBean(), pd, index);
144                 }
145             }
146         }
147         return value;
148     }
149 
150     protected boolean isActualProperty() {
151         return getPropertyDescriptor() != null;
152     }
153 
154     public boolean isCollection() {
155         PropertyDescriptor pd = getPropertyDescriptor();
156         if (pd == null) {
157             return false;
158         }
159         
160         if (pd instanceof IndexedPropertyDescriptor) {
161             return true;
162         }
163         
164         int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
165         if (hint == -1) {
166             return false;
167         }
168         if (hint == 1) {
169             return true;
170         }
171         
172         Object value = getBaseValue();
173         return value != null && ValueUtils.isCollection(value);
174     }
175     
176     /***
177      * If the property contains a collection, then the length of that
178      * collection, otherwise - 1.
179      */
180     public int getLength() {
181         PropertyDescriptor pd = getPropertyDescriptor();
182         if (pd == null) {
183             return 1;
184         }
185         
186         if (pd instanceof IndexedPropertyDescriptor) {
187             return ValueUtils.getIndexedPropertyLength(
188                 getBean(),
189                 (IndexedPropertyDescriptor) pd);
190         }
191         
192         int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
193         if (hint == -1) {
194             return 1;
195         }
196         return ValueUtils.getLength(getBaseValue());
197     }
198     
199     /***
200      * If index == WHOLE_COLLECTION, change the value of the property, otherwise
201      * change the value of the index'th element of the collection
202      * represented by the property.
203      */
204     public void setValue(Object value) {
205         PropertyDescriptor pd = getPropertyDescriptor();
206         if (pd == null) {
207             throw new JXPathException(
208                 "Cannot set property: " + asPath() + " - no such property");
209         }
210 
211         if (index == WHOLE_COLLECTION) {
212             ValueUtils.setValue(getBean(), pd, value);
213         }
214         else {
215             ValueUtils.setValue(getBean(), pd, index, value);
216         }
217         this.value = value;
218     }
219 
220     /***
221      * @see PropertyPointer#createPath(JXPathContext)
222      */
223     public NodePointer createPath(JXPathContext context) {
224         if (getImmediateNode() == null) {
225             super.createPath(context);
226             baseValue = UNINITIALIZED;
227             value = UNINITIALIZED;
228         }
229         return this;
230     }
231 
232     public void remove() {
233         if (index == WHOLE_COLLECTION) {
234             setValue(null);
235         }
236         else if (isCollection()) {
237             Object collection = ValueUtils.remove(getBaseValue(), index);
238             ValueUtils.setValue(getBean(), getPropertyDescriptor(), collection);
239         }
240         else if (index == 0) {
241             index = WHOLE_COLLECTION;
242             setValue(null);
243         }
244     }
245 
246     /***
247      * Name of the currently selected property.
248      */
249     public String getPropertyName() {
250         if (propertyName == null) {
251             PropertyDescriptor pd = getPropertyDescriptor();
252             if (pd != null) {
253                 propertyName = pd.getName();
254             }
255         }
256         return propertyName != null ? propertyName : "*";
257     }
258 
259     /***
260      * Finds the property descriptor corresponding to the current property
261      * index.
262      */
263     private PropertyDescriptor getPropertyDescriptor() {
264         if (propertyDescriptor == null) {
265             int inx = getPropertyIndex();
266             if (inx == UNSPECIFIED_PROPERTY) {
267                 propertyDescriptor =
268                     beanInfo.getPropertyDescriptor(propertyName);
269             }
270             else {
271                 PropertyDescriptor propertyDescriptors[] =
272                     getPropertyDescriptors();
273                 if (inx >= 0 && inx < propertyDescriptors.length) {
274                     propertyDescriptor = propertyDescriptors[inx];
275                 }
276                 else {
277                     propertyDescriptor = null;
278                 }
279             }
280         }
281         return propertyDescriptor;
282     }
283 
284     protected PropertyDescriptor[] getPropertyDescriptors() {
285         if (propertyDescriptors == null) {
286             propertyDescriptors = beanInfo.getPropertyDescriptors();
287         }
288         return propertyDescriptors;
289     }
290 }