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