1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.jxpath.ri.model.beans;
  17. import org.apache.commons.jxpath.JXPathException;
  18. import org.apache.commons.jxpath.ri.model.NodeIterator;
  19. import org.apache.commons.jxpath.ri.model.NodePointer;
  20. /**
  21. * Iterates property values of an object pointed at with a PropertyOwnerPointer.
  22. * Examples of such objects are JavaBeans and objects with Dynamic Properties.
  23. *
  24. * @author Dmitri Plotnikov
  25. * @version $Revision: 1.13 $ $Date: 2004/03/25 03:49:50 $
  26. */
  27. public class PropertyIterator implements NodeIterator {
  28. private boolean empty = false;
  29. private boolean reverse;
  30. private String name;
  31. private int startIndex = 0;
  32. private boolean targetReady = false;
  33. private int position = 0;
  34. private PropertyPointer propertyNodePointer;
  35. private int startPropertyIndex;
  36. private boolean ready = false;
  37. private boolean includeStart = false;
  38. public PropertyIterator(
  39. PropertyOwnerPointer pointer,
  40. String name,
  41. boolean reverse,
  42. NodePointer startWith)
  43. {
  44. propertyNodePointer =
  45. (PropertyPointer) pointer.getPropertyPointer().clone();
  46. this.name = name;
  47. this.reverse = reverse;
  48. this.includeStart = true;
  49. if (reverse) {
  50. this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
  51. this.startIndex = -1;
  52. }
  53. if (startWith != null) {
  54. while (startWith != null
  55. && startWith.getImmediateParentPointer() != pointer) {
  56. startWith = startWith.getImmediateParentPointer();
  57. }
  58. if (startWith == null) {
  59. throw new JXPathException(
  60. "PropertyIerator startWith parameter is "
  61. + "not a child of the supplied parent");
  62. }
  63. this.startPropertyIndex =
  64. ((PropertyPointer) startWith).getPropertyIndex();
  65. this.startIndex = startWith.getIndex();
  66. if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
  67. this.startIndex = 0;
  68. }
  69. this.includeStart = false;
  70. if (reverse && startIndex == -1) {
  71. this.includeStart = true;
  72. }
  73. }
  74. }
  75. protected NodePointer getPropertyPointer() {
  76. return propertyNodePointer;
  77. }
  78. public void reset() {
  79. position = 0;
  80. targetReady = false;
  81. }
  82. public NodePointer getNodePointer() {
  83. if (position == 0) {
  84. if (name != null) {
  85. if (!targetReady) {
  86. prepareForIndividualProperty(name);
  87. }
  88. // If there is no such property - return null
  89. if (empty) {
  90. return null;
  91. }
  92. }
  93. else {
  94. if (!setPosition(1)) {
  95. return null;
  96. }
  97. reset();
  98. }
  99. }
  100. try {
  101. return propertyNodePointer.getValuePointer();
  102. }
  103. catch (Throwable ex) {
  104. // @todo: should this exception be reported in any way?
  105. NullPropertyPointer npp =
  106. new NullPropertyPointer(
  107. propertyNodePointer.getImmediateParentPointer());
  108. npp.setPropertyName(propertyNodePointer.getPropertyName());
  109. npp.setIndex(propertyNodePointer.getIndex());
  110. return npp.getValuePointer();
  111. }
  112. }
  113. public int getPosition() {
  114. return position;
  115. }
  116. public boolean setPosition(int position) {
  117. if (name != null) {
  118. return setPositionIndividualProperty(position);
  119. }
  120. else {
  121. return setPositionAllProperties(position);
  122. }
  123. }
  124. private boolean setPositionIndividualProperty(int position) {
  125. this.position = position;
  126. if (position < 1) {
  127. return false;
  128. }
  129. if (!targetReady) {
  130. prepareForIndividualProperty(name);
  131. }
  132. if (empty) {
  133. return false;
  134. }
  135. int length = getLength();
  136. int index;
  137. if (!reverse) {
  138. index = position + startIndex;
  139. if (!includeStart) {
  140. index++;
  141. }
  142. if (index > length) {
  143. return false;
  144. }
  145. }
  146. else {
  147. int end = startIndex;
  148. if (end == -1) {
  149. end = length - 1;
  150. }
  151. index = end - position + 2;
  152. if (!includeStart) {
  153. index--;
  154. }
  155. if (index < 1) {
  156. return false;
  157. }
  158. }
  159. propertyNodePointer.setIndex(index - 1);
  160. return true;
  161. }
  162. private boolean setPositionAllProperties(int position) {
  163. this.position = position;
  164. if (position < 1) {
  165. return false;
  166. }
  167. int offset;
  168. int count = propertyNodePointer.getPropertyCount();
  169. if (!reverse) {
  170. int index = 1;
  171. for (int i = startPropertyIndex; i < count; i++) {
  172. propertyNodePointer.setPropertyIndex(i);
  173. int length = getLength();
  174. if (i == startPropertyIndex) {
  175. length -= startIndex;
  176. if (!includeStart) {
  177. length--;
  178. }
  179. offset = startIndex + position - index;
  180. if (!includeStart) {
  181. offset++;
  182. }
  183. }
  184. else {
  185. offset = position - index;
  186. }
  187. if (index <= position && position < index + length) {
  188. propertyNodePointer.setIndex(offset);
  189. return true;
  190. }
  191. index += length;
  192. }
  193. }
  194. else {
  195. int index = 1;
  196. int start = startPropertyIndex;
  197. if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
  198. start = count - 1;
  199. }
  200. for (int i = start; i >= 0; i--) {
  201. propertyNodePointer.setPropertyIndex(i);
  202. int length = getLength();
  203. if (i == startPropertyIndex) {
  204. int end = startIndex;
  205. if (end == -1) {
  206. end = length - 1;
  207. }
  208. length = end + 1;
  209. offset = end - position + 1;
  210. if (!includeStart) {
  211. offset--;
  212. length--;
  213. }
  214. }
  215. else {
  216. offset = length - (position - index) - 1;
  217. }
  218. if (index <= position && position < index + length) {
  219. propertyNodePointer.setIndex(offset);
  220. return true;
  221. }
  222. index += length;
  223. }
  224. }
  225. return false;
  226. }
  227. protected void prepareForIndividualProperty(String name) {
  228. targetReady = true;
  229. empty = true;
  230. String names[] = propertyNodePointer.getPropertyNames();
  231. if (!reverse) {
  232. if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
  233. startPropertyIndex = 0;
  234. }
  235. if (startIndex == NodePointer.WHOLE_COLLECTION) {
  236. startIndex = 0;
  237. }
  238. for (int i = startPropertyIndex; i < names.length; i++) {
  239. if (names[i].equals(name)) {
  240. propertyNodePointer.setPropertyIndex(i);
  241. if (i != startPropertyIndex) {
  242. startIndex = 0;
  243. includeStart = true;
  244. }
  245. empty = false;
  246. break;
  247. }
  248. }
  249. }
  250. else {
  251. if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
  252. startPropertyIndex = names.length - 1;
  253. }
  254. if (startIndex == NodePointer.WHOLE_COLLECTION) {
  255. startIndex = -1;
  256. }
  257. for (int i = startPropertyIndex; i >= 0; i--) {
  258. if (names[i].equals(name)) {
  259. propertyNodePointer.setPropertyIndex(i);
  260. if (i != startPropertyIndex) {
  261. startIndex = -1;
  262. includeStart = true;
  263. }
  264. empty = false;
  265. break;
  266. }
  267. }
  268. }
  269. }
  270. /**
  271. * Computes length for the current pointer - ignores any exceptions
  272. */
  273. private int getLength() {
  274. int length;
  275. try {
  276. length = propertyNodePointer.getLength(); // TBD: cache length
  277. }
  278. catch (Throwable t) {
  279. // @todo: should this exception be reported in any way?
  280. length = 0;
  281. }
  282. return length;
  283. }
  284. }