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 org.apache.commons.jxpath.AbstractFactory;
020 import org.apache.commons.jxpath.JXPathAbstractFactoryException;
021 import org.apache.commons.jxpath.JXPathContext;
022 import org.apache.commons.jxpath.JXPathInvalidAccessException;
023 import org.apache.commons.jxpath.ri.QName;
024 import org.apache.commons.jxpath.ri.model.NodePointer;
025
026 /**
027 * @author Dmitri Plotnikov
028 * @version $Revision: 652884 $ $Date: 2008-05-02 15:02:00 -0500 (Fri, 02 May 2008) $
029 */
030 public class NullPropertyPointer extends PropertyPointer {
031
032 private String propertyName = "*";
033 private boolean byNameAttribute = false;
034
035 private static final long serialVersionUID = 5296593071854982754L;
036
037 /**
038 * Create a new NullPropertyPointer.
039 * @param parent pointer
040 */
041 public NullPropertyPointer(NodePointer parent) {
042 super(parent);
043 }
044
045 public QName getName() {
046 return new QName(propertyName);
047 }
048
049 public void setPropertyIndex(int index) {
050 }
051
052 public int getLength() {
053 return 0;
054 }
055
056 public Object getBaseValue() {
057 return null;
058 }
059
060 public Object getImmediateNode() {
061 return null;
062 }
063
064 public boolean isLeaf() {
065 return true;
066 }
067
068 public NodePointer getValuePointer() {
069 return new NullPointer(this, new QName(getPropertyName()));
070 }
071
072 protected boolean isActualProperty() {
073 return false;
074 }
075
076 public boolean isActual() {
077 return false;
078 }
079
080 public boolean isContainer() {
081 return true;
082 }
083
084 public void setValue(Object value) {
085 if (parent == null || parent.isContainer()) {
086 throw new JXPathInvalidAccessException(
087 "Cannot set property "
088 + asPath()
089 + ", the target object is null");
090 }
091 if (parent instanceof PropertyOwnerPointer
092 && ((PropertyOwnerPointer) parent)
093 .isDynamicPropertyDeclarationSupported()) {
094 // If the parent property owner can create
095 // a property automatically - let it do so
096 PropertyPointer propertyPointer =
097 ((PropertyOwnerPointer) parent).getPropertyPointer();
098 propertyPointer.setPropertyName(propertyName);
099 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 }