    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    }