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 org.apache.commons.jxpath.AbstractFactory;
19  import org.apache.commons.jxpath.JXPathContext;
20  import org.apache.commons.jxpath.JXPathException;
21  import org.apache.commons.jxpath.JXPathIntrospector;
22  import org.apache.commons.jxpath.ri.QName;
23  import org.apache.commons.jxpath.ri.model.NodePointer;
24  import org.apache.commons.jxpath.util.ValueUtils;
25  
26  /***
27   * A pointer allocated by a PropertyOwnerPointer to represent the value of
28   * a property of the parent object.
29   *
30   * @author Dmitri Plotnikov
31   * @version $Revision: 1.14 $ $Date: 2004/04/04 22:06:36 $
32   */
33  public abstract class PropertyPointer extends NodePointer {
34      public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE;
35  
36      protected int propertyIndex = UNSPECIFIED_PROPERTY;
37      protected Object bean;
38  
39      /***
40       * Takes a javabean, a descriptor of a property of that object and
41       * an offset within that property (starting with 0).
42       */
43      public PropertyPointer(NodePointer parent) {
44          super(parent);
45      }
46  
47      public int getPropertyIndex() {
48          return propertyIndex;
49      }
50  
51      public void setPropertyIndex(int index) {
52          if (propertyIndex != index) {
53              propertyIndex = index;
54              setIndex(WHOLE_COLLECTION);
55          }
56      }
57  
58      public Object getBean() {
59          if (bean == null) {
60              bean = getImmediateParentPointer().getNode();
61          }
62          return bean;
63      }
64  
65      public QName getName() {
66          return new QName(null, getPropertyName());
67      }
68  
69      public abstract String getPropertyName();
70  
71      public abstract void setPropertyName(String propertyName);
72  
73      public abstract int getPropertyCount();
74  
75      public abstract String[] getPropertyNames();
76  
77      protected abstract boolean isActualProperty();
78  
79      public boolean isActual() {
80          if (!isActualProperty()) {
81              return false;
82          }
83  
84          return super.isActual();
85      }
86  
87      private static final Object UNINITIALIZED = new Object();
88  
89      private Object value = UNINITIALIZED;
90      public Object getImmediateNode() {
91          if (value == UNINITIALIZED) {
92              if (index == WHOLE_COLLECTION) {
93                  value = ValueUtils.getValue(getBaseValue());
94              }
95              else {
96                  value = ValueUtils.getValue(getBaseValue(), index);
97              }
98          }
99          return value;
100     }
101     
102     public boolean isCollection() {
103         Object value = getBaseValue();
104         return value != null && ValueUtils.isCollection(value);
105     }
106     
107     public boolean isLeaf() {
108         Object value = getNode();
109         return value == null
110             || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
111     }    
112 
113     /***
114      * If the property contains a collection, then the length of that
115      * collection, otherwise - 1.
116      */
117     public int getLength() {
118         return ValueUtils.getLength(getBaseValue());
119     }
120 
121 
122     /***
123      * Returns a NodePointer that can be used to access the currently
124      * selected property value.
125      */
126     public NodePointer getImmediateValuePointer() {
127         return NodePointer.newChildNodePointer(
128             this,
129             getName(),
130             getImmediateNode());
131     }
132 
133     public NodePointer createPath(JXPathContext context) {
134         if (getImmediateNode() == null) {
135             AbstractFactory factory = getAbstractFactory(context);
136             int inx = (index == WHOLE_COLLECTION ? 0 : index);
137             boolean success =
138                 factory.createObject(
139                     context,
140                     this,
141                     getBean(),
142                     getPropertyName(),
143                     inx);
144             if (!success) {
145                 throw new JXPathException(
146                     "Factory "
147                         + factory
148                         + " could not create an object for path: "
149                         + asPath());
150             }
151         }
152         return this;
153     }
154 
155     public NodePointer createPath(JXPathContext context, Object value) {
156         // If neccessary, expand collection
157         if (index != WHOLE_COLLECTION && index >= getLength()) {
158             createPath(context);
159         }
160         setValue(value);            
161         return this;
162     }
163 
164     public NodePointer createChild(
165         JXPathContext context,
166         QName name,
167         int index,
168         Object value) 
169     {
170         PropertyPointer prop = (PropertyPointer) clone();
171         if (name != null) {
172             prop.setPropertyName(name.toString());
173         }
174         prop.setIndex(index);
175         return prop.createPath(context, value);
176     }
177 
178     public NodePointer createChild(
179         JXPathContext context,
180         QName name,
181         int index) 
182     {
183         PropertyPointer prop = (PropertyPointer) clone();
184         if (name != null) {
185             prop.setPropertyName(name.toString());
186         }
187         prop.setIndex(index);
188         return prop.createPath(context);
189     }
190 
191     public int hashCode() {
192         return getImmediateParentPointer().hashCode() + propertyIndex + index;
193     }
194 
195     public boolean equals(Object object) {
196         if (object == this) {
197             return true;
198         }
199 
200         if (!(object instanceof PropertyPointer)) {
201             return false;
202         }
203 
204         PropertyPointer other = (PropertyPointer) object;
205         if (parent != other.parent) {
206             if (parent == null || !parent.equals(other.parent)) {
207                 return false;
208             }
209         }
210 
211         if (getPropertyIndex() != other.getPropertyIndex()
212             || !getPropertyName().equals(other.getPropertyName())) {
213             return false;
214         }
215 
216         int iThis = (index == WHOLE_COLLECTION ? 0 : index);
217         int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index);
218         return iThis == iOther;
219     }
220 
221     public int compareChildNodePointers(
222         NodePointer pointer1,
223         NodePointer pointer2) 
224     {
225         return getValuePointer().compareChildNodePointers(pointer1, pointer2);
226     }
227     
228     private AbstractFactory getAbstractFactory(JXPathContext context) {
229         AbstractFactory factory = context.getFactory();
230         if (factory == null) {
231             throw new JXPathException(
232                 "Factory is not set on the "
233                     + "JXPathContext - cannot create path: "
234                     + asPath());
235         }
236         return factory;
237     }
238 }