/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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;
/**
* Filtered view implementation. Works by composing a view of the document, and
* filters out elements based on what the method {@link #getSkipLevel(Object)},
* says about them. Subclass to implement that method.
* TODO(danilatos): Change to composition of a getSkipLevel interface rather than
* using inheritance.
*
* @author danilatos@google.com (Daniel Danilatos)
*
* @param <N>
* @param <E>
* @param <T>
*/
public abstract class FilteredView<N, E extends N, T extends N>
extends IdentityView<N, E, T>
implements ReadableDocumentView<N, E, T> {
/**
* @author danilatos@google.com (Daniel Danilatos)
*/
public enum Skip {
/** Do not skip */
NONE,
/** Skip, but traverse into children */
SHALLOW,
/** Skip including entire subtree rooted at node */
DEEP,
/** Invalid - e.g. in the html outside the document */
INVALID
}
protected abstract Skip getSkipLevel(N node);
/**
* Constructor
* @param innerView View we are boxing
*/
public FilteredView(ReadableDocument<N, E, T> innerView) {
super(innerView);
}
@Override
public N getFirstChild(N node) {
N find = inner.getFirstChild(node);
return getNextVisibleNodeDepthFirst(find, node, true);
}
@Override
public N getLastChild(N node) {
N find = inner.getLastChild(node);
return getPreviousVisibleNodeDepthFirst(find, node, true);
}
@Override
public N getNextSibling(N node) {
E parent = getParentElement(node);
N find = getNextNodeDepthFirst(inner, node, parent, false);
return getNextVisibleNodeDepthFirst(find, parent, true);
}
@Override
public N getPreviousSibling(N node) {
E parent = getParentElement(node);
N find = getPrevNodeDepthFirst(inner, node, parent, false);
return getPreviousVisibleNodeDepthFirst(find, parent, true);
}
@Override
public E getParentElement(N node) {
E element = inner.getParentElement(node);
while (element != null) {
switch (getSkipLevel(element)) {
case NONE:
return element;
case SHALLOW:
element = inner.getParentElement(element);
break;
case DEEP:
// Shouldn't really happen, but better handle it
element = inner.getParentElement(element);
break;
case INVALID:
return null;
default:
throw new RuntimeException("Unimplemented");
}
}
return null;
}
@Override
public N getVisibleNodeNext(N node) {
return getNextVisibleNodeDepthFirst(node, null, false);
}
@Override
public N getVisibleNodePrevious(N node) {
return getPreviousVisibleNodeDepthFirst(node, null, false);
}
@Override
public N getVisibleNodeFirst(N node) {
return getNextVisibleNodeDepthFirst(node, null, true);
}
@Override
public N getVisibleNodeLast(N node) {
return getPreviousVisibleNodeDepthFirst(node, null, true);
}
@Override
public N getVisibleNode(N node) {
if (node == null) {
return node;
}
switch (getSkipLevel(node)) {
case NONE:
return node;
case SHALLOW:
case DEEP:
return getParentElement(node);
case INVALID:
return null;
default:
throw new RuntimeException("Unimplemented");
}
}
@Override
public void onBeforeFilter(Point<N> at) {
// default = do nothing.
}
// Helpers
private N getNextVisibleNodeDepthFirst(N find, N stopAt, boolean enterFirst) {
boolean enter = enterFirst;
while (find != null) {
switch (getSkipLevel(find)) {
case NONE:
return find;
case SHALLOW:
find = getNextNodeDepthFirst(inner, find, stopAt, enter);
break;
case DEEP:
find = getNextNodeDepthFirst(inner, find, stopAt, false);
break;
case INVALID:
return null;
default:
throw new RuntimeException("Unimplemented");
}
enter = true;
}
return null;
}
private N getPreviousVisibleNodeDepthFirst(N find, N stopAt, boolean enterFirst) {
boolean enter = enterFirst;
while (find != null) {
switch (getSkipLevel(find)) {
case NONE:
return find;
case SHALLOW:
find = getPrevNodeDepthFirst(inner, find, stopAt, enter);
break;
case DEEP:
find = getPrevNodeDepthFirst(inner, find, stopAt, false);
break;
case INVALID:
return null;
default:
throw new RuntimeException("Unimplemented");
}
enter = true;
}
return null;
}
private N getNextNodeDepthFirst(
ReadableDocument<N, E, T> doc, N start, N stopAt, boolean enter) {
return getNextOrPrevNodeDepthFirst(doc, start, stopAt, enter, true);
}
private N getPrevNodeDepthFirst(
ReadableDocument<N, E, T> doc, N start, N stopAt, boolean enter) {
return getNextOrPrevNodeDepthFirst(doc, start, stopAt, enter, false);
}
protected N getNextOrPrevNodeDepthFirst(
ReadableDocument<N, E, T> doc, N start, N stopAt, boolean enter, boolean rightwards) {
return DocHelper.getNextOrPrevNodeDepthFirst(doc, start, stopAt, enter, rightwards);
}
}