/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.model.document.util;
import org.waveprotocol.wave.model.document.ReadableDocument;
import java.util.Iterator;
/**
* Helpers for iterating through documents
*
* @author danilatos@google.com (Daniel Danilatos)
*/
public class DocIterate {
/**
* @param <V> Value to map the nodes to.
* @param <N> Node type in the document this filter is over.
* @param <E> Element type in the document this filter is over.
* @param <T> Text node type in the document this filter is over.
*/
public interface DocIterationFilter<V, N, E extends N, T extends N> {
/**
* @param doc useful to keep most implementations as singletons
* @param current the current node
* @param stopAt the node to stop at (exclude)
* @return the next node in the iteration
*/
N next(ReadableDocument<N, E, T> doc, N current, N stopAt);
/**
* @param doc useful to keep most implementations as singletons
* @param node
* @return the value the node should map to
*/
V value(ReadableDocument<N, E, T> doc, N node);
}
/**
* Iterates using through the document using a DocIterationFilter.
*
* @param doc
* @param startNode
* @param iterateFunction
* @param stopAt the exact meaning depends on the given iteration function, in
* particular whether the node is excluded on the way in or on the way
* out of traversal
*/
public static <V, N, E extends N, T extends N> Iterable<V> iterate(
final ReadableDocument<N, E, T> doc, final N startNode, final N stopAt,
final DocIterationFilter<V, N, E, T> iterateFunction) {
return new Iterable<V>() {
@Override
public Iterator<V> iterator() {
return new Iterator<V>() {
N next = startNode;
@Override
public boolean hasNext() {
return next != null;
}
@Override
public V next() {
N currentNode = next;
assert currentNode != null;
next = iterateFunction.next(doc, currentNode, stopAt);
return iterateFunction.value(doc, currentNode);
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
};
}
/*
* Filters
*/
/** Depth-first recursive iterator that navigates forwards or backwards through the tree. */
static class DeepIteratorFilter implements DocIterationFilter<Object, Object, Object, Object> {
private final boolean rightwards;
DeepIteratorFilter(boolean rightwards) {
this.rightwards = rightwards; // store direction.
}
@Override
public Object next(ReadableDocument<Object, Object, Object> doc,
Object current, Object stopAt) {
return DocHelper.getNextOrPrevNodeDepthFirst(doc, current, stopAt, true, rightwards);
}
@Override
public Object value(ReadableDocument<Object, Object, Object> doc, Object node) {
return node;
}
}
/** Deep Iterator that skips text nodes. */
static class ElementFilter extends DeepIteratorFilter {
public ElementFilter(boolean rightwards) {
super(rightwards);
}
@Override
public Object next(ReadableDocument<Object, Object, Object> doc,
Object current, Object stopAt) {
Object next = super.next(doc, current, stopAt);
while (next != null && doc.asElement(next) == null) {
next = super.next(doc, next, stopAt);
}
return next;
}
@Override
public Object value(ReadableDocument<Object, Object, Object> doc, Object node) {
Object ret = doc.asElement(super.value(doc, node));
assert ret != null;
return ret;
}
}
/**
* Iteration filter that filters out elements by tag name.
*/
static class ElementByTagNameFilter extends ElementFilter {
private final String tagName;
public ElementByTagNameFilter(String tagName, boolean rightwards) {
super(rightwards);
this.tagName = tagName;
}
@Override
public Object next(ReadableDocument<Object, Object, Object> doc,
Object current, Object stopAt) {
Object next = super.next(doc, current, stopAt);
while (next != null && !doc.getTagName(doc.asElement(next)).equals(tagName)) {
next = super.next(doc, next, stopAt);
}
return next;
}
}
/*
* Overly generic (Object-based) implementations
* NOTE(patcoleman): where possible , access by the static casting getter methods versions.
*/
/** Depth first forwards/backwards traversal of the tree nodes. */
static final DocIterationFilter<Object, Object, Object, Object>
FORWARD_DEPTH_FIRST_ITERATOR = new DeepIteratorFilter(true);
static final DocIterationFilter<Object, Object, Object, Object>
REVERSE_DEPTH_FIRST_ITERATOR = new DeepIteratorFilter(false);
/** Backwards and forwards element iterator filters. */
static final DocIterationFilter<Object, Object, Object, Object>
FORWARD_DEPTH_FIRST_ELEMENT_ITERATOR = new ElementFilter(true);
static final DocIterationFilter<Object, Object, Object, Object>
REVERSE_DEPTH_FIRST_ELEMENT_ITERATOR = new ElementFilter(false);
/*
* Casting wrappers for typesafe iteration. See funge section at the end for sources.
* All return DocIterationFilter<?,?,?,?>
*/
/** @return A Forward depth first iterator bound to an (N, E, T) type tuple. */
@SuppressWarnings("unchecked")
public static <N, E extends N, T extends N>
DocIterationFilter<N, N, E, T> forwardDepthFirstIterator() {
return (DocIterationFilter<N, N, E, T>) fungeForwardDepthFirstIterator();
}
/** @return A Backwards depth first iterator bound to an (N, E, T) type tuple. */
@SuppressWarnings("unchecked")
public static <N, E extends N, T extends N>
DocIterationFilter<N, N, E, T> reverseDepthFirstIterator() {
return (DocIterationFilter<N, N, E, T>) fungeReverseDepthFirstIterator();
}
/** @return A Forward depth first iterator bound to an (N, E, T) type tuple. */
@SuppressWarnings("unchecked")
public static <N, E extends N, T extends N>
DocIterationFilter<E, N, E, T> forwardDepthFirstElementIterator() {
return (DocIterationFilter<E, N, E, T>) fungeForwardDepthFirstElementIterator();
}
/** @return A Backwards depth first iterator bound to an (N, E, T) type tuple. */
@SuppressWarnings("unchecked")
public static <N, E extends N, T extends N>
DocIterationFilter<E, N, E, T> reverseDepthFirstElementIterator() {
return (DocIterationFilter<E, N, E, T>) fungeReverseDepthFirstElementIterator();
}
/** @return A Forwards depth first element iterator bound to an (N, E, T) type tuple. */
@SuppressWarnings("unchecked")
public static <N, E extends N, T extends N>
DocIterationFilter<E, N, E, T> forwardDepthFirstElementByTagNameIterator(String tag) {
return (DocIterationFilter<E, N, E, T>) fungeForwardDepthFirstElementTagNameIterator(tag);
}
/*
* Iterators - all of these should return Iterable<?>
*/
/**
* Iterates using
* {@link DocHelper#getNextNodeDepthFirst(ReadableDocument, Object, Object, boolean)}
* The parameters to this method match those for that method.
*
* @param doc
* @param startNode
* @param stopAt
*/
public static <N, E extends N, T extends N> Iterable<N> deep(
final ReadableDocument<N, E, T> doc, final N startNode, final N stopAt) {
return iterate(doc, startNode, stopAt, DocIterate.<N, E, T>forwardDepthFirstIterator());
}
/**
* Iterates using
* {@link DocHelper#getPrevNodeDepthFirst(ReadableDocument, Object, Object, boolean)}
* The parameters to this method match those for that method.
*
* @param doc
* @param startNode
* @param stopAt
*/
public static <N, E extends N, T extends N> Iterable<N> deepReverse(
final ReadableDocument<N, E, T> doc, final N startNode, final N stopAt) {
return iterate(doc, startNode, stopAt, DocIterate.<N, E, T>reverseDepthFirstIterator());
}
/**
* Same as {@link #deep(ReadableDocument, Object, Object)}, but filters out
* non-elements
*/
public static <N, E extends N, T extends N> Iterable<E> deepElements(
final ReadableDocument<N, E, T> doc, final E startNode, final E stopAt) {
return iterate(doc, startNode, stopAt, DocIterate.<N, E, T>forwardDepthFirstElementIterator());
}
/**
* Same as {@link #deepReverse(ReadableDocument, Object, Object)}, but filters out
* non-elements
*/
public static <N, E extends N, T extends N> Iterable<E> deepElementsReverse(
final ReadableDocument<N, E, T> doc, final E startNode, final E stopAt) {
return iterate(doc, startNode, stopAt, DocIterate.<N, E, T>reverseDepthFirstElementIterator());
}
/** See {@link #deepElementsWithTagName(ReadableDocument, String, Object, Object)}. */
public static <N, E extends N, T extends N> Iterable<E> deepElementsWithTagName(
final ReadableDocument<N, E, T> doc, String tagName) {
return deepElementsWithTagName(
doc, tagName, DocHelper.getElementWithTagName(doc, tagName), null);
}
/**
* Same as {@link #deepElements(ReadableDocument, Object, Object)}, but filters out
* elements that do not match the given tag name.
*
* @return an iterable used for iterating matching elements.
*/
public static <N, E extends N, T extends N> Iterable<E> deepElementsWithTagName(
final ReadableDocument<N, E, T> doc, String tagName, final E startNode, final E stopAt) {
return iterate(doc, startNode, stopAt,
DocIterate.<N, E, T>forwardDepthFirstElementByTagNameIterator(tagName));
}
/*
* Funge methods for unfurling generics, required only for Sun JDK compiler.
*/
/** @return A Forward depth first filter funged to the type N. */
@SuppressWarnings("unchecked")
private static <N> DocIterationFilter<?, N, ?, ?> fungeForwardDepthFirstIterator() {
return (DocIterationFilter<?, N, ?, ?>) FORWARD_DEPTH_FIRST_ITERATOR;
}
/** @return A Backwards depth first filter funged to the type N. */
@SuppressWarnings("unchecked")
private static <N> DocIterationFilter<?, N, ?, ?> fungeReverseDepthFirstIterator() {
return (DocIterationFilter<?, N, ?, ?>) REVERSE_DEPTH_FIRST_ITERATOR;
}
/** @return A Forward depth first element filter funged to the type N. */
@SuppressWarnings("unchecked")
private static <N> DocIterationFilter<?, N, ?, ?> fungeForwardDepthFirstElementIterator() {
return (DocIterationFilter<?, N, ?, ?>) FORWARD_DEPTH_FIRST_ELEMENT_ITERATOR;
}
/** @return A Backwards depth first element filter funged to the type N. */
@SuppressWarnings("unchecked")
private static <N> DocIterationFilter<?, N, ?, ?> fungeReverseDepthFirstElementIterator() {
return (DocIterationFilter<?, N, ?, ?>) REVERSE_DEPTH_FIRST_ELEMENT_ITERATOR;
}
/** @return A Forward depth first element filter by tag name, funged to the type N. */
@SuppressWarnings("unchecked")
private static <N> DocIterationFilter<?, N, ?, ?> fungeForwardDepthFirstElementTagNameIterator(
String tagName) {
// NOTE(patcoleman): Must remain on two lines so the compiler knows what it's doing.
// Here we have to funge an ElementByTagName filter into a DocIterationFilter, then funge in
// the N type, then funge in the remainding generics. Fun.
DocIterationFilter<Object, Object, Object, Object> filter
= new ElementByTagNameFilter(tagName, true);
return (DocIterationFilter<?, N, ?, ?>) filter;
}
}