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.JXPathException; 020import org.apache.commons.jxpath.ri.model.NodeIterator; 021import org.apache.commons.jxpath.ri.model.NodePointer; 022 023/** 024 * Iterates property values of an object pointed at with a {@link PropertyOwnerPointer}. 025 * Examples of such objects are JavaBeans and objects with Dynamic Properties. 026 * 027 * @author Dmitri Plotnikov 028 * @version $Revision: 917247 $ $Date: 2010-02-28 19:47:00 +0100 (So, 28 Feb 2010) $ 029 */ 030public class PropertyIterator implements NodeIterator { 031 private boolean empty = false; 032 private boolean reverse; 033 private String name; 034 private int startIndex = 0; 035 private boolean targetReady = false; 036 private int position = 0; 037 private PropertyPointer propertyNodePointer; 038 private int startPropertyIndex; 039 040 private boolean includeStart = false; 041 042 /** 043 * Create a new PropertyIterator. 044 * @param pointer owning pointer 045 * @param name property name 046 * @param reverse iteration order 047 * @param startWith beginning pointer 048 */ 049 public PropertyIterator( 050 PropertyOwnerPointer pointer, 051 String name, 052 boolean reverse, 053 NodePointer startWith) { 054 propertyNodePointer = 055 (PropertyPointer) pointer.getPropertyPointer().clone(); 056 this.name = name; 057 this.reverse = reverse; 058 this.includeStart = true; 059 if (reverse) { 060 this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY; 061 this.startIndex = -1; 062 } 063 if (startWith != null) { 064 while (startWith != null 065 && startWith.getImmediateParentPointer() != pointer) { 066 startWith = startWith.getImmediateParentPointer(); 067 } 068 if (startWith == null) { 069 throw new JXPathException( 070 "PropertyIerator startWith parameter is " 071 + "not a child of the supplied parent"); 072 } 073 this.startPropertyIndex = 074 ((PropertyPointer) startWith).getPropertyIndex(); 075 this.startIndex = startWith.getIndex(); 076 if (this.startIndex == NodePointer.WHOLE_COLLECTION) { 077 this.startIndex = 0; 078 } 079 this.includeStart = false; 080 if (reverse && startIndex == -1) { 081 this.includeStart = true; 082 } 083 } 084 } 085 086 /** 087 * Get the property pointer. 088 * @return NodePointer 089 */ 090 protected NodePointer getPropertyPointer() { 091 return propertyNodePointer; 092 } 093 094 /** 095 * Reset property iteration. 096 */ 097 public void reset() { 098 position = 0; 099 targetReady = false; 100 } 101 102 public NodePointer getNodePointer() { 103 if (position == 0) { 104 if (name != null) { 105 if (!targetReady) { 106 prepareForIndividualProperty(name); 107 } 108 // If there is no such property - return null 109 if (empty) { 110 return null; 111 } 112 } 113 else { 114 if (!setPosition(1)) { 115 return null; 116 } 117 reset(); 118 } 119 } 120 try { 121 return propertyNodePointer.getValuePointer(); 122 } 123 catch (Throwable t) { 124 propertyNodePointer.handle(t); 125 NullPropertyPointer npp = 126 new NullPropertyPointer( 127 propertyNodePointer.getImmediateParentPointer()); 128 npp.setPropertyName(propertyNodePointer.getPropertyName()); 129 npp.setIndex(propertyNodePointer.getIndex()); 130 return npp.getValuePointer(); 131 } 132 } 133 134 public int getPosition() { 135 return position; 136 } 137 138 public boolean setPosition(int position) { 139 return name == null ? setPositionAllProperties(position) : setPositionIndividualProperty(position); 140 } 141 142 /** 143 * Set position for an individual property. 144 * @param position int position 145 * @return whether this was a valid position 146 */ 147 private boolean setPositionIndividualProperty(int position) { 148 this.position = position; 149 if (position < 1) { 150 return false; 151 } 152 153 if (!targetReady) { 154 prepareForIndividualProperty(name); 155 } 156 157 if (empty) { 158 return false; 159 } 160 161 int length = getLength(); 162 int index; 163 if (!reverse) { 164 index = position + startIndex; 165 if (!includeStart) { 166 index++; 167 } 168 if (index > length) { 169 return false; 170 } 171 } 172 else { 173 int end = startIndex; 174 if (end == -1) { 175 end = length - 1; 176 } 177 index = end - position + 2; 178 if (!includeStart) { 179 index--; 180 } 181 if (index < 1) { 182 return false; 183 } 184 } 185 propertyNodePointer.setIndex(index - 1); 186 return true; 187 } 188 189 /** 190 * Set position for all properties 191 * @param position int position 192 * @return whether this was a valid position 193 */ 194 private boolean setPositionAllProperties(int position) { 195 this.position = position; 196 if (position < 1) { 197 return false; 198 } 199 200 int offset; 201 int count = propertyNodePointer.getPropertyCount(); 202 if (!reverse) { 203 int index = 1; 204 for (int i = startPropertyIndex; i < count; i++) { 205 propertyNodePointer.setPropertyIndex(i); 206 int length = getLength(); 207 if (i == startPropertyIndex) { 208 length -= startIndex; 209 if (!includeStart) { 210 length--; 211 } 212 offset = startIndex + position - index; 213 if (!includeStart) { 214 offset++; 215 } 216 } 217 else { 218 offset = position - index; 219 } 220 if (index <= position && position < index + length) { 221 propertyNodePointer.setIndex(offset); 222 return true; 223 } 224 index += length; 225 } 226 } 227 else { 228 int index = 1; 229 int start = startPropertyIndex; 230 if (start == PropertyPointer.UNSPECIFIED_PROPERTY) { 231 start = count - 1; 232 } 233 for (int i = start; i >= 0; i--) { 234 propertyNodePointer.setPropertyIndex(i); 235 int length = getLength(); 236 if (i == startPropertyIndex) { 237 int end = startIndex; 238 if (end == -1) { 239 end = length - 1; 240 } 241 length = end + 1; 242 offset = end - position + 1; 243 if (!includeStart) { 244 offset--; 245 length--; 246 } 247 } 248 else { 249 offset = length - (position - index) - 1; 250 } 251 252 if (index <= position && position < index + length) { 253 propertyNodePointer.setIndex(offset); 254 return true; 255 } 256 index += length; 257 } 258 } 259 return false; 260 } 261 262 /** 263 * Prepare for an individual property. 264 * @param name property name 265 */ 266 protected void prepareForIndividualProperty(String name) { 267 targetReady = true; 268 empty = true; 269 270 String[] names = propertyNodePointer.getPropertyNames(); 271 if (!reverse) { 272 if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) { 273 startPropertyIndex = 0; 274 } 275 if (startIndex == NodePointer.WHOLE_COLLECTION) { 276 startIndex = 0; 277 } 278 for (int i = startPropertyIndex; i < names.length; i++) { 279 if (names[i].equals(name)) { 280 propertyNodePointer.setPropertyIndex(i); 281 if (i != startPropertyIndex) { 282 startIndex = 0; 283 includeStart = true; 284 } 285 empty = false; 286 break; 287 } 288 } 289 } 290 else { 291 if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) { 292 startPropertyIndex = names.length - 1; 293 } 294 if (startIndex == NodePointer.WHOLE_COLLECTION) { 295 startIndex = -1; 296 } 297 for (int i = startPropertyIndex; i >= 0; i--) { 298 if (names[i].equals(name)) { 299 propertyNodePointer.setPropertyIndex(i); 300 if (i != startPropertyIndex) { 301 startIndex = -1; 302 includeStart = true; 303 } 304 empty = false; 305 break; 306 } 307 } 308 } 309 } 310 311 /** 312 * Computes length for the current pointer - ignores any exceptions. 313 * @return length 314 */ 315 private int getLength() { 316 int length; 317 try { 318 length = propertyNodePointer.getLength(); // TBD: cache length 319 } 320 catch (Throwable t) { 321 propertyNodePointer.handle(t); 322 length = 0; 323 } 324 return length; 325 } 326}