    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 java.util.Locale;
    020
    021    import org.apache.commons.jxpath.JXPathInvalidAccessException;
    022    import org.apache.commons.jxpath.ri.Compiler;
    023    import org.apache.commons.jxpath.ri.QName;
    024    import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
    025    import org.apache.commons.jxpath.ri.compiler.NodeTest;
    026    import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
    027    import org.apache.commons.jxpath.ri.model.NodeIterator;
    028    import org.apache.commons.jxpath.ri.model.NodePointer;
    029    import org.apache.commons.jxpath.util.ValueUtils;
    030
    031    /**
    032     * A pointer describing a node that has properties, each of which could be
    033     * a collection.
    034     *
    035     * @author Dmitri Plotnikov
    036     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    037     */
    038    public abstract class PropertyOwnerPointer extends NodePointer {
    039        private static final Object UNINITIALIZED = new Object();
    040
    041        private Object value = UNINITIALIZED;
    042
    043        public NodeIterator childIterator(NodeTest test, boolean reverse,
    044                NodePointer startWith) {
    045            if (test == null) {
    046                return createNodeIterator(null, reverse, startWith);
    047            }
    048            if (test instanceof NodeNameTest) {
    049                NodeNameTest nodeNameTest = (NodeNameTest) test;
    050                QName testName = nodeNameTest.getNodeName();
    051                if (isValidProperty(testName)) {
    052                    return createNodeIterator(nodeNameTest.isWildcard() ? null
    053                            : testName.toString(), reverse, startWith);
    054                }
    055                return null;
    056            }
    057            return test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE
    058                    ? createNodeIterator(null, reverse, startWith) : null;
    059        }
    060
    061        /**
    062         * Create a NodeIterator.
    063         * @param property property name
    064         * @param reverse whether to iterate in reverse
    065         * @param startWith first pointer to return
    066         * @return NodeIterator
    067         */
    068        public NodeIterator createNodeIterator(String property, boolean reverse,
    069                NodePointer startWith) {
    070            return new PropertyIterator(this, property, reverse, startWith);
    071        }
    072
    073        public NodeIterator attributeIterator(QName name) {
    074            return new BeanAttributeIterator(this, name);
    075        }
    076
    077        /**
    078         * Create a new PropertyOwnerPointer.
    079         * @param parent parent pointer
    080         * @param locale Locale
    081         */
    082        protected PropertyOwnerPointer(NodePointer parent, Locale locale) {
    083            super(parent, locale);
    084        }
    085
    086        /**
    087         * Create a new PropertyOwnerPointer.
    088         * @param parent pointer
    089         */
    090        protected PropertyOwnerPointer(NodePointer parent) {
    091            super(parent);
    092        }
    093
    094        public void setIndex(int index) {
    095            if (this.index != index) {
    096                super.setIndex(index);
    097                value = UNINITIALIZED;
    098            }
    099        }
    100
    101        public Object getImmediateNode() {
    102            if (value == UNINITIALIZED) {
    103                value = index == WHOLE_COLLECTION ? ValueUtils.getValue(getBaseValue())
    104                        : ValueUtils.getValue(getBaseValue(), index);
    105            }
    106            return value;
    107        }
    108
    109        public abstract QName getName();
    110
    111        /**
    112         * Learn whether <code>name</code> is a valid child name for this PropertyOwnerPointer.
    113         * @param name the QName to test
    114         * @return <code>true</code> if <code>QName</code> is a valid property name.
    115         * @since JXPath 1.3
    116         */
    117        public boolean isValidProperty(QName name) {
    118            return isDefaultNamespace(name.getPrefix());
    119        }
    120
    121        /**
    122         * Throws an exception if you try to change the root element, otherwise
    123         * forwards the call to the parent pointer.
    124         * @param value to set
    125         */
    126        public void setValue(Object value) {
    127            this.value = value;
    128            if (parent != null) {
    129                if (parent.isContainer()) {
    130                    parent.setValue(value);
    131                }
    132                else {
    133                    if (index == WHOLE_COLLECTION) {
    134                        throw new UnsupportedOperationException(
    135                            "Cannot setValue of an object that is not "
    136                                + "some other object's property");
    137                    }
    138                    throw new JXPathInvalidAccessException(
    139                        "The specified collection element does not exist: " + this);
    140                }
    141            }
    142            else {
    143                throw new UnsupportedOperationException(
    144                    "Cannot replace the root object");
    145            }
    146        }
    147
    148        /**
    149         * If this is a root node pointer, throws an exception; otherwise
    150         * forwards the call to the parent node.
    151         */
    152        public void remove() {
    153            this.value = null;
    154            if (parent != null) {
    155                parent.remove();
    156            }
    157            else {
    158                throw new UnsupportedOperationException(
    159                    "Cannot remove an object that is not "
    160                        + "some other object's property or a collection element");
    161            }
    162        }
    163
    164        /**
    165         * Get a PropertyPointer for this PropertyOwnerPointer.
    166         * @return PropertyPointer
    167         */
    168        public abstract PropertyPointer getPropertyPointer();
    169
    170        /**
    171         * Learn whether dynamic property declaration is supported.
    172         * @return true if the property owner can set a property "does not exist".
    173         *         A good example is a Map. You can always assign a value to any
    174         *         key even if it has never been "declared".
    175         */
    176        public boolean isDynamicPropertyDeclarationSupported() {
    177            return false;
    178        }
    179
    180        public int compareChildNodePointers(NodePointer pointer1,
    181                NodePointer pointer2) {
    182            int r = pointer1.getName().toString().compareTo(pointer2.getName().toString());
    183            return r == 0 ? pointer1.getIndex() - pointer2.getIndex() : r;
    184        }
    185    }