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.jdom; 019 020import java.util.Collections; 021import java.util.List; 022 023import org.apache.commons.jxpath.ri.compiler.NodeTest; 024import org.apache.commons.jxpath.ri.model.NodeIterator; 025import org.apache.commons.jxpath.ri.model.NodePointer; 026import org.jdom.Document; 027import org.jdom.Element; 028 029/** 030 * An iterator of children of a JDOM Node. 031 */ 032public class JDOMNodeIterator implements NodeIterator { 033 034 private final NodePointer parent; 035 private final NodeTest nodeTest; 036 private final boolean reverse; 037 private int position; 038 private int index; 039 private List children; 040 private Object child; 041 042 /** 043 * Constructs a new JDOMNodeIterator. 044 * 045 * @param parent pointer 046 * @param nodeTest test 047 * @param reverse whether to iterate in reverse 048 * @param startWith starting pointer 049 */ 050 public JDOMNodeIterator(final NodePointer parent, final NodeTest nodeTest, final boolean reverse, final NodePointer startWith) { 051 this.parent = parent; 052 if (startWith != null) { 053 this.child = startWith.getNode(); 054 } 055 // TBD: optimize me for different node tests 056 final Object node = parent.getNode(); 057 if (node instanceof Document) { 058 this.children = ((Document) node).getContent(); 059 } else if (node instanceof Element) { 060 this.children = ((Element) node).getContent(); 061 } else { 062 this.children = Collections.EMPTY_LIST; 063 } 064 this.nodeTest = nodeTest; 065 this.reverse = reverse; 066 } 067 068 @Override 069 public NodePointer getNodePointer() { 070 if (child == null) { 071 if (!setPosition(1)) { 072 return null; 073 } 074 position = 0; 075 } 076 return new JDOMNodePointer(parent, child); 077 } 078 079 @Override 080 public int getPosition() { 081 return position; 082 } 083 084 /** 085 * Iterate to next pointer. 086 * 087 * @return whether valid 088 */ 089 private boolean next() { 090 position++; 091 if (!reverse) { 092 if (position == 1) { 093 index = 0; 094 if (child != null) { 095 index = children.indexOf(child) + 1; 096 } 097 } else { 098 index++; 099 } 100 for (; index < children.size(); index++) { 101 child = children.get(index); 102 if (testChild()) { 103 return true; 104 } 105 } 106 return false; 107 } 108 if (position == 1) { 109 index = children.size() - 1; 110 if (child != null) { 111 index = children.indexOf(child) - 1; 112 } 113 } else { 114 index--; 115 } 116 for (; index >= 0; index--) { 117 child = children.get(index); 118 if (testChild()) { 119 return true; 120 } 121 } 122 return false; 123 } 124 125 /** 126 * This is actually never invoked during the normal evaluation of xpaths - an iterator is always going forward, never backwards. So, this is implemented 127 * only for completeness and perhaps for those who use these iterators outside of XPath evaluation. 128 * 129 * @return boolean 130 */ 131 private boolean previous() { 132 position--; 133 if (!reverse) { 134 while (--index >= 0) { 135 child = children.get(index); 136 if (testChild()) { 137 return true; 138 } 139 } 140 } else { 141 for (; index < children.size(); index++) { 142 child = children.get(index); 143 if (testChild()) { 144 return true; 145 } 146 } 147 } 148 return false; 149 } 150 151 @Override 152 public boolean setPosition(final int position) { 153 while (this.position < position) { 154 if (!next()) { 155 return false; 156 } 157 } 158 while (this.position > position) { 159 if (!previous()) { 160 return false; 161 } 162 } 163 return true; 164 } 165 166 /** 167 * Test a child node. 168 * 169 * @return whether test passes. 170 */ 171 private boolean testChild() { 172 return JDOMNodePointer.testNode(parent, child, nodeTest); 173 } 174}