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 }