View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jxpath.ri.model.beans;
18  
19  import java.util.Locale;
20  
21  import org.apache.commons.jxpath.JXPathInvalidAccessException;
22  import org.apache.commons.jxpath.ri.Compiler;
23  import org.apache.commons.jxpath.ri.QName;
24  import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
25  import org.apache.commons.jxpath.ri.compiler.NodeTest;
26  import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
27  import org.apache.commons.jxpath.ri.model.NodeIterator;
28  import org.apache.commons.jxpath.ri.model.NodePointer;
29  import org.apache.commons.jxpath.util.ValueUtils;
30  
31  /**
32   * A pointer describing a node that has properties, each of which could be
33   * a collection.
34   *
35   * @author Dmitri Plotnikov
36   * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
37   */
38  public abstract class PropertyOwnerPointer extends NodePointer {
39      private static final Object UNINITIALIZED = new Object();
40  
41      private Object value = UNINITIALIZED;
42  
43      public NodeIterator childIterator(NodeTest test, boolean reverse,
44              NodePointer startWith) {
45          if (test == null) {
46              return createNodeIterator(null, reverse, startWith);
47          }
48          if (test instanceof NodeNameTest) {
49              NodeNameTest nodeNameTest = (NodeNameTest) test;
50              QName testName = nodeNameTest.getNodeName();
51              if (isValidProperty(testName)) {
52                  return createNodeIterator(nodeNameTest.isWildcard() ? null
53                          : testName.toString(), reverse, startWith);
54              }
55              return null;
56          }
57          return test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE
58                  ? createNodeIterator(null, reverse, startWith) : null;
59      }
60  
61      /**
62       * Create a NodeIterator.
63       * @param property property name
64       * @param reverse whether to iterate in reverse
65       * @param startWith first pointer to return
66       * @return NodeIterator
67       */
68      public NodeIterator createNodeIterator(String property, boolean reverse,
69              NodePointer startWith) {
70          return new PropertyIterator(this, property, reverse, startWith);
71      }
72  
73      public NodeIterator attributeIterator(QName name) {
74          return new BeanAttributeIterator(this, name);
75      }
76  
77      /**
78       * Create a new PropertyOwnerPointer.
79       * @param parent parent pointer
80       * @param locale Locale
81       */
82      protected PropertyOwnerPointer(NodePointer parent, Locale locale) {
83          super(parent, locale);
84      }
85  
86      /**
87       * Create a new PropertyOwnerPointer.
88       * @param parent pointer
89       */
90      protected PropertyOwnerPointer(NodePointer parent) {
91          super(parent);
92      }
93  
94      public void setIndex(int index) {
95          if (this.index != index) {
96              super.setIndex(index);
97              value = UNINITIALIZED;
98          }
99      }
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 }