001: /*
002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Sam
028: */
029:
030: package com.caucho.quercus.lib.spl;
031:
032: import com.caucho.quercus.annotation.Name;
033: import com.caucho.quercus.annotation.Optional;
034: import com.caucho.quercus.env.*;
035: import com.caucho.util.L10N;
036:
037: import java.util.ArrayList;
038: import java.util.EnumSet;
039:
040: public class RecursiveIteratorIterator implements OuterIterator {
041: private final static L10N L = new L10N(
042: RecursiveIteratorIterator.class);
043:
044: public static final int LEAVES_ONLY = 0;
045: public static final int SELF_FIRST = 1;
046: public static final int CHILD_FIRST = 2;
047:
048: public static final int CATCH_GET_CHILD = 16;
049:
050: private final Env _env;
051:
052: private enum Mode {
053: LEAVES_ONLY, SELF_FIRST, CHILD_FIRST
054: }
055:
056: private enum Flags {
057: CATCH_GET_CHILD
058: }
059:
060: private final Mode _mode;
061: private final EnumSet<Flags> _flags;
062:
063: private int _maxDepth = -1;
064:
065: private Stack _stack = new Stack();
066:
067: /**
068: * @param iter a {@link RecursiveIterator}
069: *
070: * @param mode
071: * <dl>
072: * <dt>LEAVES_ONLY</dt>
073: * <dd>(default) only iterate leaves</dd>
074: * <dt><dt>SELF_FIRST</dt></dt>
075: * <dd>iterate parents prior to children</dd>
076: * <dt>CHILD_FIRST </dt>
077: * <dd>iterate children prior to parents</dd>
078: * </dl>
079: *
080: * @param flags
081: * <dl>
082: * <dt>CATCH_GET_CHILD</dt>
083: * <dd>ignore exceptions in getChildren() call</dd>
084: * </dl>
085: */
086: @Name("__construct")
087: public RecursiveIteratorIterator(Env env, ObjectValue iter,
088: @Optional("")
089: int mode, @Optional("0")
090: int flags) {
091: _env = env;
092: _stack.add(new StackEntry(iter));
093:
094: switch (mode) {
095: case LEAVES_ONLY:
096: _mode = Mode.LEAVES_ONLY;
097: break;
098: case SELF_FIRST:
099: _mode = Mode.SELF_FIRST;
100: break;
101: case CHILD_FIRST:
102: _mode = Mode.CHILD_FIRST;
103: break;
104: default:
105: _mode = Mode.LEAVES_ONLY;
106: break;
107: }
108:
109: if ((flags & CATCH_GET_CHILD) != 0)
110: _flags = EnumSet.of(Flags.CATCH_GET_CHILD);
111: else
112: _flags = EnumSet.noneOf(Flags.class);
113: }
114:
115: /**
116: * Template method provided for overriding classes, called right after
117: * getChildren() and rewind() on the child, default implemenation does
118: * nothing.
119: */
120: public void beginChildren() {
121: }
122:
123: /**
124: * Template method provided for overriding classes, default implementation does
125: * nothing.
126: */
127: public void beginIteration() {
128: }
129:
130: /**
131: * Returns current iterator's children.
132: */
133: public Value callGetChildren() {
134: return _stack.getLast().getChildren();
135: }
136:
137: /**
138: * Returns true if current iterator children.
139: */
140: public boolean callHasChildren() {
141: return _stack.getLast().hasChildren();
142: }
143:
144: public Value current() {
145: return _stack.getLast().current();
146: }
147:
148: /**
149: * Template method provided for overriding classes, called after
150: * an iteterator has been used up.
151: */
152: public void endChildren() {
153: }
154:
155: /**
156: * Template method provided for overriding classe, default implementation does
157: * nothing.
158: */
159: public void endIteration() {
160: }
161:
162: /**
163: * Returns the current depth.
164: */
165: public int getDepth() {
166: return _stack.size() - 1;
167: }
168:
169: public ObjectValue getInnerIterator() {
170: return null;
171: }
172:
173: /**
174: * Returns the maximum depth.
175: */
176: public int getMaxDepth() {
177: return _maxDepth;
178: }
179:
180: /**
181: * Returns the iterator at the given depth
182: * @param depth the depth, default is current depth
183: */
184: public Value getSubIterator(@Optional("-1")
185: int depth) {
186: if (depth == -1)
187: return _stack.getLast().getIterator();
188: else if (_stack.size() <= depth)
189: return UnsetValue.UNSET;
190: else
191: return _stack.get(depth).getIterator();
192: }
193:
194: public Value key() {
195: return _stack.getLast().key();
196: }
197:
198: public void next() {
199: StackEntry last = _stack.getLast();
200:
201: last.next();
202: }
203:
204: /**
205: * Template method provided for overriding classes, called when
206: * the next element is available
207: */
208: public void nextElement() {
209: }
210:
211: /**
212: * Rewind to the first iterator, calling endChildren() along the way
213: * as appropriate.
214: */
215: public void rewind() {
216: while (_stack.size() > 1) {
217: _stack.removeLast();
218: endChildren();
219: }
220:
221: _stack.getLast().rewind();
222: }
223:
224: /**
225: * Sets the maximum depth.
226: */
227: public void setMaxDepth(int maxDepth) {
228: _maxDepth = maxDepth;
229: }
230:
231: /**
232: * Return true if the iterator is valid.
233: */
234: public boolean valid() {
235: for (int i = _stack.size() - 1; i >= 0; i--) {
236: if (_stack.get(i).valid())
237: return true;
238: }
239:
240: return false;
241: }
242:
243: private class Stack extends ArrayList<StackEntry> {
244: public StackEntry getLast() {
245: return get(size() - 1);
246: }
247:
248: public StackEntry removeLast() {
249: return remove(size() - 1);
250: }
251: }
252:
253: private class StackEntry {
254: final ObjectValue _iterator;
255:
256: public StackEntry(ObjectValue obj) {
257: _iterator = obj;
258: }
259:
260: public ObjectValue getIterator() {
261: return _iterator;
262: }
263:
264: public Value current() {
265: return _iterator.callMethod(_env, _CURRENT);
266: }
267:
268: public Value key() {
269: return _iterator.callMethod(_env, _KEY);
270: }
271:
272: public void next() {
273: _iterator.callMethod(_env, _NEXT);
274: }
275:
276: public void rewind() {
277: _iterator.callMethod(_env, _REWIND);
278: }
279:
280: public boolean valid() {
281: return _iterator.callMethod(_env, _VALID).toBoolean();
282: }
283:
284: public boolean hasChildren() {
285: return _iterator.callMethod(_env, _HAS_CHILDREN)
286: .toBoolean();
287: }
288:
289: public Value getChildren() {
290: return _iterator.callMethod(_env, _GET_CHILDREN);
291: }
292: }
293:
294: private static final StringValue _GET_CHILDREN = new StringBuilderValue(
295: "getChildren");
296:
297: private static final StringValue _HAS_CHILDREN = new StringBuilderValue(
298: "hasChildren");
299:
300: private static final StringValue _KEY = new StringBuilderValue(
301: "next");
302:
303: private static final StringValue _NEXT = new StringBuilderValue(
304: "next");
305:
306: private static final StringValue _CURRENT = new StringBuilderValue(
307: "current");
308:
309: private static final StringValue _REWIND = new StringBuilderValue(
310: "rewind");
311:
312: private static final StringValue _VALID = new StringBuilderValue(
313: "valid");
314: }
|