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    }