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 org.apache.commons.jxpath.AbstractFactory;
20  import org.apache.commons.jxpath.JXPathAbstractFactoryException;
21  import org.apache.commons.jxpath.JXPathContext;
22  import org.apache.commons.jxpath.JXPathInvalidAccessException;
23  import org.apache.commons.jxpath.ri.QName;
24  import org.apache.commons.jxpath.ri.model.NodePointer;
25  
26  /**
27   * @author Dmitri Plotnikov
28   * @version $Revision: 652884 $ $Date: 2008-05-02 22:02:00 +0200 (Fr, 02 Mai 2008) $
29   */
30  public class NullPropertyPointer extends PropertyPointer {
31  
32      private String propertyName = "*";
33      private boolean byNameAttribute = false;
34  
35      private static final long serialVersionUID = 5296593071854982754L;
36  
37      /**
38       * Create a new NullPropertyPointer.
39       * @param parent pointer
40       */
41      public NullPropertyPointer(NodePointer parent) {
42          super(parent);
43      }
44  
45      public QName getName() {
46          return new QName(propertyName);
47      }
48  
49      public void setPropertyIndex(int index) {
50      }
51  
52      public int getLength() {
53          return 0;
54      }
55  
56      public Object getBaseValue() {
57          return null;
58      }
59  
60      public Object getImmediateNode() {
61          return null;
62      }
63  
64      public boolean isLeaf() {
65          return true;
66      }
67  
68      public NodePointer getValuePointer() {
69          return new NullPointer(this,  new QName(getPropertyName()));
70      }
71  
72      protected boolean isActualProperty() {
73          return false;
74      }
75  
76      public boolean isActual() {
77          return false;
78      }
79  
80      public boolean isContainer() {
81          return true;
82      }
83  
84      public void setValue(Object value) {
85          if (parent == null || parent.isContainer()) {
86              throw new JXPathInvalidAccessException(
87                  "Cannot set property "
88                      + asPath()
89                      + ", the target object is null");
90          }
91          if (parent instanceof PropertyOwnerPointer
92                  && ((PropertyOwnerPointer) parent)
93                          .isDynamicPropertyDeclarationSupported()) {
94              // If the parent property owner can create
95              // a property automatically - let it do so
96              PropertyPointer propertyPointer =
97                  ((PropertyOwnerPointer) parent).getPropertyPointer();
98              propertyPointer.setPropertyName(propertyName);
99              propertyPointer.setValue(value);
100         }
101         else {
102             throw new JXPathInvalidAccessException(
103                 "Cannot set property "
104                     + asPath()
105                     + ", path does not match a changeable location");
106         }
107     }
108 
109     public NodePointer createPath(JXPathContext context) {
110         NodePointer newParent = parent.createPath(context);
111         if (isAttribute()) {
112             return newParent.createAttribute(context, getName());
113         }
114         if (parent instanceof NullPointer && parent.equals(newParent)) {
115             throw createBadFactoryException(context.getFactory());
116         }
117         // Consider these two use cases:
118         // 1. The parent pointer of NullPropertyPointer is
119         //    a PropertyOwnerPointer other than NullPointer. When we call
120         //    createPath on it, it most likely returns itself. We then
121         //    take a PropertyPointer from it and get the PropertyPointer
122         //    to expand the collection for the corresponding property.
123         //
124         // 2. The parent pointer of NullPropertyPointer is a NullPointer.
125         //    When we call createPath, it may return a PropertyOwnerPointer
126         //    or it may return anything else, like a DOMNodePointer.
127         //    In the former case we need to do exactly what we did in use
128         //    case 1.  In the latter case, we simply request that the
129         //    non-property pointer expand the collection by itself.
130         if (newParent instanceof PropertyOwnerPointer) {
131             PropertyOwnerPointer pop = (PropertyOwnerPointer) newParent;
132             newParent = pop.getPropertyPointer();
133         }
134         return newParent.createChild(context, getName(), getIndex());
135     }
136 
137     public NodePointer createPath(JXPathContext context, Object value) {
138         NodePointer newParent = parent.createPath(context);
139         if (isAttribute()) {
140             NodePointer pointer = newParent.createAttribute(context, getName());
141             pointer.setValue(value);
142             return pointer;
143         }
144         if (parent instanceof NullPointer && parent.equals(newParent)) {
145             throw createBadFactoryException(context.getFactory());
146         }
147         if (newParent instanceof PropertyOwnerPointer) {
148             PropertyOwnerPointer pop = (PropertyOwnerPointer) newParent;
149             newParent = pop.getPropertyPointer();
150         }
151         return newParent.createChild(context, getName(), index, value);
152     }
153 
154     public NodePointer createChild(JXPathContext context, QName name, int index) {
155         return createPath(context).createChild(context, name, index);
156     }
157 
158     public NodePointer createChild(JXPathContext context, QName name,
159             int index, Object value) {
160         return createPath(context).createChild(context, name, index, value);
161     }
162 
163     public String getPropertyName() {
164         return propertyName;
165     }
166 
167     public void setPropertyName(String propertyName) {
168         this.propertyName = propertyName;
169     }
170 
171     /**
172      * Set the name attribute.
173      * @param attributeValue value to set
174      */
175     public void setNameAttributeValue(String attributeValue) {
176         this.propertyName = attributeValue;
177         byNameAttribute = true;
178     }
179 
180     public boolean isCollection() {
181         return getIndex() != WHOLE_COLLECTION;
182     }
183 
184     public int getPropertyCount() {
185         return 0;
186     }
187 
188     public String[] getPropertyNames() {
189         return new String[0];
190     }
191 
192     public String asPath() {
193         if (!byNameAttribute) {
194             return super.asPath();
195         }
196         StringBuffer buffer = new StringBuffer();
197         buffer.append(getImmediateParentPointer().asPath());
198         buffer.append("[@name='");
199         buffer.append(escape(getPropertyName()));
200         buffer.append("']");
201         if (index != WHOLE_COLLECTION) {
202             buffer.append('[').append(index + 1).append(']');
203         }
204         return buffer.toString();
205     }
206 
207     /**
208      * Create a "bad factory" JXPathAbstractFactoryException for the specified AbstractFactory.
209      * @param factory AbstractFactory
210      * @return JXPathAbstractFactoryException
211      */
212     private JXPathAbstractFactoryException createBadFactoryException(AbstractFactory factory) {
213         return new JXPathAbstractFactoryException("Factory " + factory
214                 + " reported success creating object for path: " + asPath()
215                 + " but object was null.  Terminating to avoid stack recursion.");
216     }
217 }