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.JXPathIntrospector; 023 import org.apache.commons.jxpath.ri.QName; 024 import org.apache.commons.jxpath.ri.model.NodePointer; 025 import org.apache.commons.jxpath.util.ValueUtils; 026 027 /** 028 * A pointer allocated by a PropertyOwnerPointer to represent the value of 029 * a property of the parent object. 030 * 031 * @author Dmitri Plotnikov 032 * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $ 033 */ 034 public abstract class PropertyPointer extends NodePointer { 035 public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE; 036 037 /** property index */ 038 protected int propertyIndex = UNSPECIFIED_PROPERTY; 039 040 /** owning object */ 041 protected Object bean; 042 043 /** 044 * Takes a javabean, a descriptor of a property of that object and 045 * an offset within that property (starting with 0). 046 * @param parent parent pointer 047 */ 048 public PropertyPointer(NodePointer parent) { 049 super(parent); 050 } 051 052 /** 053 * Get the property index. 054 * @return int index 055 */ 056 public int getPropertyIndex() { 057 return propertyIndex; 058 } 059 060 /** 061 * Set the property index. 062 * @param index property index 063 */ 064 public void setPropertyIndex(int index) { 065 if (propertyIndex != index) { 066 propertyIndex = index; 067 setIndex(WHOLE_COLLECTION); 068 } 069 } 070 071 /** 072 * Get the parent bean. 073 * @return Object 074 */ 075 public Object getBean() { 076 if (bean == null) { 077 bean = getImmediateParentPointer().getNode(); 078 } 079 return bean; 080 } 081 082 public QName getName() { 083 return new QName(null, getPropertyName()); 084 } 085 086 /** 087 * Get the property name. 088 * @return String property name. 089 */ 090 public abstract String getPropertyName(); 091 092 /** 093 * Set the property name. 094 * @param propertyName property name to set. 095 */ 096 public abstract void setPropertyName(String propertyName); 097 098 /** 099 * Count the number of properties represented. 100 * @return int 101 */ 102 public abstract int getPropertyCount(); 103 104 /** 105 * Get the names of the included properties. 106 * @return String[] 107 */ 108 public abstract String[] getPropertyNames(); 109 110 /** 111 * Learn whether this pointer references an actual property. 112 * @return true if actual 113 */ 114 protected abstract boolean isActualProperty(); 115 116 public boolean isActual() { 117 if (!isActualProperty()) { 118 return false; 119 } 120 121 return super.isActual(); 122 } 123 124 private static final Object UNINITIALIZED = new Object(); 125 126 private Object value = UNINITIALIZED; 127 128 public Object getImmediateNode() { 129 if (value == UNINITIALIZED) { 130 value = index == WHOLE_COLLECTION ? ValueUtils.getValue(getBaseValue()) 131 : ValueUtils.getValue(getBaseValue(), index); 132 } 133 return value; 134 } 135 136 public boolean isCollection() { 137 Object value = getBaseValue(); 138 return value != null && ValueUtils.isCollection(value); 139 } 140 141 public boolean isLeaf() { 142 Object value = getNode(); 143 return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic(); 144 } 145 146 /** 147 * If the property contains a collection, then the length of that 148 * collection, otherwise - 1. 149 * @return int length 150 */ 151 public int getLength() { 152 return ValueUtils.getLength(getBaseValue()); 153 } 154 155 /** 156 * Returns a NodePointer that can be used to access the currently 157 * selected property value. 158 * @return NodePointer 159 */ 160 public NodePointer getImmediateValuePointer() { 161 return NodePointer.newChildNodePointer( 162 (NodePointer) this.clone(), 163 getName(), 164 getImmediateNode()); 165 } 166 167 public NodePointer createPath(JXPathContext context) { 168 if (getImmediateNode() == null) { 169 AbstractFactory factory = getAbstractFactory(context); 170 int inx = (index == WHOLE_COLLECTION ? 0 : index); 171 boolean success = 172 factory.createObject( 173 context, 174 this, 175 getBean(), 176 getPropertyName(), 177 inx); 178 if (!success) { 179 throw new JXPathAbstractFactoryException("Factory " + factory 180 + " could not create an object for path: " + asPath()); 181 } 182 } 183 return this; 184 } 185 186 public NodePointer createPath(JXPathContext context, Object value) { 187 // If neccessary, expand collection 188 if (index != WHOLE_COLLECTION && index >= getLength()) { 189 createPath(context); 190 } 191 setValue(value); 192 return this; 193 } 194 195 public NodePointer createChild( 196 JXPathContext context, 197 QName name, 198 int index, 199 Object value) { 200 PropertyPointer prop = (PropertyPointer) clone(); 201 if (name != null) { 202 prop.setPropertyName(name.toString()); 203 } 204 prop.setIndex(index); 205 return prop.createPath(context, value); 206 } 207 208 public NodePointer createChild( 209 JXPathContext context, 210 QName name, 211 int index) { 212 PropertyPointer prop = (PropertyPointer) clone(); 213 if (name != null) { 214 prop.setPropertyName(name.toString()); 215 } 216 prop.setIndex(index); 217 return prop.createPath(context); 218 } 219 220 public int hashCode() { 221 return getImmediateParentPointer().hashCode() + propertyIndex + index; 222 } 223 224 public boolean equals(Object object) { 225 if (object == this) { 226 return true; 227 } 228 229 if (!(object instanceof PropertyPointer)) { 230 return false; 231 } 232 233 PropertyPointer other = (PropertyPointer) object; 234 if (parent != other.parent && (parent == null || !parent.equals(other.parent))) { 235 return false; 236 } 237 238 if (getPropertyIndex() != other.getPropertyIndex() 239 || !getPropertyName().equals(other.getPropertyName())) { 240 return false; 241 } 242 243 int iThis = (index == WHOLE_COLLECTION ? 0 : index); 244 int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index); 245 return iThis == iOther; 246 } 247 248 public int compareChildNodePointers( 249 NodePointer pointer1, 250 NodePointer pointer2) { 251 return getValuePointer().compareChildNodePointers(pointer1, pointer2); 252 } 253 254 }