View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jxpath.ri.model.beans;
18  
19  import org.apache.commons.jxpath.AbstractFactory;
20  import org.apache.commons.jxpath.JXPathAbstractFactoryException;
21  import org.apache.commons.jxpath.JXPathContext;
22  import org.apache.commons.jxpath.JXPathIntrospector;
23  import org.apache.commons.jxpath.ri.QName;
24  import org.apache.commons.jxpath.ri.model.NodePointer;
25  import org.apache.commons.jxpath.util.ValueUtils;
26  
27  /**
28   * A pointer allocated by a PropertyOwnerPointer to represent the value of
29   * a property of the parent object.
30   *
31   * @author Dmitri Plotnikov
32   * @version $Revision: 1133499 $ $Date: 2011-06-08 20:19:50 +0200 (Mi, 08 Jun 2011) $
33   */
34  public abstract class PropertyPointer extends NodePointer {
35      public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE;
36  
37      /** property index */
38      protected int propertyIndex = UNSPECIFIED_PROPERTY;
39  
40      /** owning object */
41      protected Object bean;
42  
43      /**
44       * Takes a javabean, a descriptor of a property of that object and
45       * an offset within that property (starting with 0).
46       * @param parent parent pointer
47       */
48      public PropertyPointer(NodePointer parent) {
49          super(parent);
50      }
51  
52      /**
53       * Get the property index.
54       * @return int index
55       */
56      public int getPropertyIndex() {
57          return propertyIndex;
58      }
59  
60      /**
61       * Set the property index.
62       * @param index property index
63       */
64      public void setPropertyIndex(int index) {
65          if (propertyIndex != index) {
66              propertyIndex = index;
67              setIndex(WHOLE_COLLECTION);
68          }
69      }
70  
71      /**
72       * Get the parent bean.
73       * @return Object
74       */
75      public Object getBean() {
76          if (bean == null) {
77              bean = getImmediateParentPointer().getNode();
78          }
79          return bean;
80      }
81  
82      public QName getName() {
83          return new QName(null, getPropertyName());
84      }
85  
86      /**
87       * Get the property name.
88       * @return String property name.
89       */
90      public abstract String getPropertyName();
91  
92      /**
93       * Set the property name.
94       * @param propertyName property name to set.
95       */
96      public abstract void setPropertyName(String propertyName);
97  
98      /**
99       * Count the number of properties represented.
100      * @return int
101      */
102     public abstract int getPropertyCount();
103 
104     /**
105      * Get the names of the included properties.
106      * @return String[]
107      */
108     public abstract String[] getPropertyNames();
109 
110     /**
111      * Learn whether this pointer references an actual property.
112      * @return true if actual
113      */
114     protected abstract boolean isActualProperty();
115 
116     public boolean isActual() {
117         if (!isActualProperty()) {
118             return false;
119         }
120 
121         return super.isActual();
122     }
123 
124     private static final Object UNINITIALIZED = new Object();
125 
126     private Object value = UNINITIALIZED;
127 
128     public Object getImmediateNode() {
129         if (value == UNINITIALIZED) {
130             value = index == WHOLE_COLLECTION ? ValueUtils.getValue(getBaseValue())
131                     : ValueUtils.getValue(getBaseValue(), index);
132         }
133         return value;
134     }
135 
136     public boolean isCollection() {
137         Object value = getBaseValue();
138         return value != null && ValueUtils.isCollection(value);
139     }
140 
141     public boolean isLeaf() {
142         Object value = getNode();
143         return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
144     }
145 
146     /**
147      * If the property contains a collection, then the length of that
148      * collection, otherwise - 1.
149      * @return int length
150      */
151     public int getLength() {
152         Object baseValue = getBaseValue();
153         return baseValue == null ? 1 : ValueUtils.getLength(baseValue);
154     }
155 
156     /**
157      * Returns a NodePointer that can be used to access the currently
158      * selected property value.
159      * @return NodePointer
160      */
161     public NodePointer getImmediateValuePointer() {
162         return NodePointer.newChildNodePointer(
163             (NodePointer) this.clone(),
164             getName(),
165             getImmediateNode());
166     }
167 
168     public NodePointer createPath(JXPathContext context) {
169         if (getImmediateNode() == null) {
170             AbstractFactory factory = getAbstractFactory(context);
171             int inx = (index == WHOLE_COLLECTION ? 0 : index);
172             boolean success =
173                 factory.createObject(
174                     context,
175                     this,
176                     getBean(),
177                     getPropertyName(),
178                     inx);
179             if (!success) {
180                 throw new JXPathAbstractFactoryException("Factory " + factory
181                         + " could not create an object for path: " + asPath());
182             }
183         }
184         return this;
185     }
186 
187     public NodePointer createPath(JXPathContext context, Object value) {
188         // If neccessary, expand collection
189         if (index != WHOLE_COLLECTION && index >= getLength()) {
190             createPath(context);
191         }
192         setValue(value);
193         return this;
194     }
195 
196     public NodePointer createChild(
197         JXPathContext context,
198         QName name,
199         int index,
200         Object value) {
201         PropertyPointer prop = (PropertyPointer) clone();
202         if (name != null) {
203             prop.setPropertyName(name.toString());
204         }
205         prop.setIndex(index);
206         return prop.createPath(context, value);
207     }
208 
209     public NodePointer createChild(
210         JXPathContext context,
211         QName name,
212         int index) {
213         PropertyPointer prop = (PropertyPointer) clone();
214         if (name != null) {
215             prop.setPropertyName(name.toString());
216         }
217         prop.setIndex(index);
218         return prop.createPath(context);
219     }
220 
221     public int hashCode() {
222         return getImmediateParentPointer().hashCode() + propertyIndex + index;
223     }
224 
225     public boolean equals(Object object) {
226         if (object == this) {
227             return true;
228         }
229 
230         if (!(object instanceof PropertyPointer)) {
231             return false;
232         }
233 
234         PropertyPointer other = (PropertyPointer) object;
235         if (parent != other.parent && (parent == null || !parent.equals(other.parent))) {
236             return false;
237         }
238 
239         if (getPropertyIndex() != other.getPropertyIndex()
240             || !getPropertyName().equals(other.getPropertyName())) {
241             return false;
242         }
243 
244         int iThis = (index == WHOLE_COLLECTION ? 0 : index);
245         int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index);
246         return iThis == iOther;
247     }
248 
249     public int compareChildNodePointers(
250         NodePointer pointer1,
251         NodePointer pointer2) {
252         return getValuePointer().compareChildNodePointers(pointer1, pointer2);
253     }
254 
255 }