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 java.util.Locale;
020
021import org.apache.commons.jxpath.JXPathInvalidAccessException;
022import org.apache.commons.jxpath.ri.Compiler;
023import org.apache.commons.jxpath.ri.QName;
024import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
025import org.apache.commons.jxpath.ri.compiler.NodeTest;
026import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
027import org.apache.commons.jxpath.ri.model.NodeIterator;
028import org.apache.commons.jxpath.ri.model.NodePointer;
029import 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 19:46:46 +0200 (Fr, 02 Mai 2008) $
037 */
038public 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}