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