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.JXPathIntrospector; 023import org.apache.commons.jxpath.ri.QName; 024import org.apache.commons.jxpath.ri.model.NodePointer; 025import 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: 1133499 $ $Date: 2011-06-08 20:19:50 +0200 (Mi, 08 Jun 2011) $ 033 */ 034public 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 Object baseValue = getBaseValue(); 153 return baseValue == null ? 1 : ValueUtils.getLength(baseValue); 154 } 155 156 /** 157 * Returns a NodePointer that can be used to access the currently 158 * selected property value. 159 * @return NodePointer 160 */ 161 public NodePointer getImmediateValuePointer() { 162 return NodePointer.newChildNodePointer( 163 (NodePointer) this.clone(), 164 getName(), 165 getImmediateNode()); 166 } 167 168 public NodePointer createPath(JXPathContext context) { 169 if (getImmediateNode() == null) { 170 AbstractFactory factory = getAbstractFactory(context); 171 int inx = (index == WHOLE_COLLECTION ? 0 : index); 172 boolean success = 173 factory.createObject( 174 context, 175 this, 176 getBean(), 177 getPropertyName(), 178 inx); 179 if (!success) { 180 throw new JXPathAbstractFactoryException("Factory " + factory 181 + " could not create an object for path: " + asPath()); 182 } 183 } 184 return this; 185 } 186 187 public NodePointer createPath(JXPathContext context, Object value) { 188 // If neccessary, expand collection 189 if (index != WHOLE_COLLECTION && index >= getLength()) { 190 createPath(context); 191 } 192 setValue(value); 193 return this; 194 } 195 196 public NodePointer createChild( 197 JXPathContext context, 198 QName name, 199 int index, 200 Object value) { 201 PropertyPointer prop = (PropertyPointer) clone(); 202 if (name != null) { 203 prop.setPropertyName(name.toString()); 204 } 205 prop.setIndex(index); 206 return prop.createPath(context, value); 207 } 208 209 public NodePointer createChild( 210 JXPathContext context, 211 QName name, 212 int index) { 213 PropertyPointer prop = (PropertyPointer) clone(); 214 if (name != null) { 215 prop.setPropertyName(name.toString()); 216 } 217 prop.setIndex(index); 218 return prop.createPath(context); 219 } 220 221 public int hashCode() { 222 return getImmediateParentPointer().hashCode() + propertyIndex + index; 223 } 224 225 public boolean equals(Object object) { 226 if (object == this) { 227 return true; 228 } 229 230 if (!(object instanceof PropertyPointer)) { 231 return false; 232 } 233 234 PropertyPointer other = (PropertyPointer) object; 235 if (parent != other.parent && (parent == null || !parent.equals(other.parent))) { 236 return false; 237 } 238 239 if (getPropertyIndex() != other.getPropertyIndex() 240 || !getPropertyName().equals(other.getPropertyName())) { 241 return false; 242 } 243 244 int iThis = (index == WHOLE_COLLECTION ? 0 : index); 245 int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index); 246 return iThis == iOther; 247 } 248 249 public int compareChildNodePointers( 250 NodePointer pointer1, 251 NodePointer pointer2) { 252 return getValuePointer().compareChildNodePointers(pointer1, pointer2); 253 } 254 255}