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 */
017 package org.apache.commons.jxpath.ri.model.beans;
018
019 import org.apache.commons.jxpath.AbstractFactory;
020 import org.apache.commons.jxpath.JXPathAbstractFactoryException;
021 import org.apache.commons.jxpath.JXPathContext;
022 import org.apache.commons.jxpath.JXPathIntrospector;
023 import org.apache.commons.jxpath.ri.QName;
024 import org.apache.commons.jxpath.ri.model.NodePointer;
025 import org.apache.commons.jxpath.util.ValueUtils;
026
027 /**
028 * A pointer allocated by a PropertyOwnerPointer to represent the value of
029 * a property of the parent object.
030 *
031 * @author Dmitri Plotnikov
032 * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
033 */
034 public abstract class PropertyPointer extends NodePointer {
035 public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE;
036
037 /** property index */
038 protected int propertyIndex = UNSPECIFIED_PROPERTY;
039
040 /** owning object */
041 protected Object bean;
042
043 /**
044 * Takes a javabean, a descriptor of a property of that object and
045 * an offset within that property (starting with 0).
046 * @param parent parent pointer
047 */
048 public PropertyPointer(NodePointer parent) {
049 super(parent);
050 }
051
052 /**
053 * Get the property index.
054 * @return int index
055 */
056 public int getPropertyIndex() {
057 return propertyIndex;
058 }
059
060 /**
061 * Set the property index.
062 * @param index property index
063 */
064 public void setPropertyIndex(int index) {
065 if (propertyIndex != index) {
066 propertyIndex = index;
067 setIndex(WHOLE_COLLECTION);
068 }
069 }
070
071 /**
072 * Get the parent bean.
073 * @return Object
074 */
075 public Object getBean() {
076 if (bean == null) {
077 bean = getImmediateParentPointer().getNode();
078 }
079 return bean;
080 }
081
082 public QName getName() {
083 return new QName(null, getPropertyName());
084 }
085
086 /**
087 * Get the property name.
088 * @return String property name.
089 */
090 public abstract String getPropertyName();
091
092 /**
093 * Set the property name.
094 * @param propertyName property name to set.
095 */
096 public abstract void setPropertyName(String propertyName);
097
098 /**
099 * 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 return ValueUtils.getLength(getBaseValue());
153 }
154
155 /**
156 * Returns a NodePointer that can be used to access the currently
157 * selected property value.
158 * @return NodePointer
159 */
160 public NodePointer getImmediateValuePointer() {
161 return NodePointer.newChildNodePointer(
162 (NodePointer) this.clone(),
163 getName(),
164 getImmediateNode());
165 }
166
167 public NodePointer createPath(JXPathContext context) {
168 if (getImmediateNode() == null) {
169 AbstractFactory factory = getAbstractFactory(context);
170 int inx = (index == WHOLE_COLLECTION ? 0 : index);
171 boolean success =
172 factory.createObject(
173 context,
174 this,
175 getBean(),
176 getPropertyName(),
177 inx);
178 if (!success) {
179 throw new JXPathAbstractFactoryException("Factory " + factory
180 + " could not create an object for path: " + asPath());
181 }
182 }
183 return this;
184 }
185
186 public NodePointer createPath(JXPathContext context, Object value) {
187 // If neccessary, expand collection
188 if (index != WHOLE_COLLECTION && index >= getLength()) {
189 createPath(context);
190 }
191 setValue(value);
192 return this;
193 }
194
195 public NodePointer createChild(
196 JXPathContext context,
197 QName name,
198 int index,
199 Object value) {
200 PropertyPointer prop = (PropertyPointer) clone();
201 if (name != null) {
202 prop.setPropertyName(name.toString());
203 }
204 prop.setIndex(index);
205 return prop.createPath(context, value);
206 }
207
208 public NodePointer createChild(
209 JXPathContext context,
210 QName name,
211 int index) {
212 PropertyPointer prop = (PropertyPointer) clone();
213 if (name != null) {
214 prop.setPropertyName(name.toString());
215 }
216 prop.setIndex(index);
217 return prop.createPath(context);
218 }
219
220 public int hashCode() {
221 return getImmediateParentPointer().hashCode() + propertyIndex + index;
222 }
223
224 public boolean equals(Object object) {
225 if (object == this) {
226 return true;
227 }
228
229 if (!(object instanceof PropertyPointer)) {
230 return false;
231 }
232
233 PropertyPointer other = (PropertyPointer) object;
234 if (parent != other.parent && (parent == null || !parent.equals(other.parent))) {
235 return false;
236 }
237
238 if (getPropertyIndex() != other.getPropertyIndex()
239 || !getPropertyName().equals(other.getPropertyName())) {
240 return false;
241 }
242
243 int iThis = (index == WHOLE_COLLECTION ? 0 : index);
244 int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index);
245 return iThis == iOther;
246 }
247
248 public int compareChildNodePointers(
249 NodePointer pointer1,
250 NodePointer pointer2) {
251 return getValuePointer().compareChildNodePointers(pointer1, pointer2);
252 }
253
254 }