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 org.apache.commons.jxpath.AbstractFactory;
020import org.apache.commons.jxpath.JXPathAbstractFactoryException;
021import org.apache.commons.jxpath.JXPathContext;
022import org.apache.commons.jxpath.JXPathIntrospector;
023import org.apache.commons.jxpath.ri.QName;
024import org.apache.commons.jxpath.ri.model.NodePointer;
025import 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 */
034public 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}