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 */ 017package org.apache.commons.jxpath.ri.model.beans; 018 019import org.apache.commons.jxpath.AbstractFactory; 020import org.apache.commons.jxpath.JXPathAbstractFactoryException; 021import org.apache.commons.jxpath.JXPathContext; 022import org.apache.commons.jxpath.JXPathInvalidAccessException; 023import org.apache.commons.jxpath.ri.QName; 024import org.apache.commons.jxpath.ri.model.NodePointer; 025 026/** 027 * @author Dmitri Plotnikov 028 * @version $Revision: 652884 $ $Date: 2008-05-02 22:02:00 +0200 (Fr, 02 Mai 2008) $ 029 */ 030public 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}