/*
* Copyright 2010 Ronnie Kolehmainen
*
* 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 com.github.cssxfire.tree;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: Ronnie
*/
public class TreeUtils {
/**
* Find the declaration node which either preceeds or follows the given anchor.
*
* @param root the tree root
* @param anchor the anchor (optional)
* @param direction - a positive value means forward direction, a negative value means backwards
* @return the next (or previous) declaration node relative to the anchor, or <tt>null</tt> if no declaration node found.
*/
@Nullable
public static CssDeclarationNode seek(@NotNull CssTreeNode root, @Nullable CssTreeNode anchor, int direction) {
if (anchor == null) {
// seek to first leaf
return (CssDeclarationNode) iterateLeafs(root).iterator().next();
}
List<CssTreeNode> flattened = flatten(root);
if (direction < 0) {
Collections.reverse(flattened);
}
boolean anchorFound = false;
for (CssTreeNode node : flattened) {
if (node == anchor) {
anchorFound = true;
continue;
}
if (anchorFound && node instanceof CssDeclarationNode) {
return (CssDeclarationNode) node;
}
}
// Not found after the anchor, now try from start
for (CssTreeNode node : flattened) {
if (node instanceof CssDeclarationNode) {
return (CssDeclarationNode) node;
}
}
return null;
}
private static List<CssTreeNode> flatten(@NotNull CssTreeNode root) {
List<CssTreeNode> flattened = new ArrayList<CssTreeNode>();
Enumeration enumeration = root.breadthFirstEnumeration();
while (enumeration.hasMoreElements()) {
flattened.add((CssTreeNode) enumeration.nextElement());
}
return flattened;
}
public static int countLeafs(CssTreeNode root) {
int numLeafs = 0;
for (CssTreeNode leaf : iterateLeafs(root)) {
numLeafs++;
}
return numLeafs;
}
public static Iterable<CssTreeNode> iterateLeafs(CssTreeNode root) {
return new CssTreeLeafIterable(root);
}
private static class CssTreeLeafIterable implements Iterable<CssTreeNode> {
private final LeafIterator leafIterator;
private CssTreeLeafIterable(CssTreeNode root) {
this.leafIterator = new LeafIterator(root);
}
public Iterator<CssTreeNode> iterator() {
return leafIterator;
}
private class LeafIterator implements Iterator<CssTreeNode> {
private Enumeration enumeration;
private CssTreeNode next;
public LeafIterator(CssTreeNode root) {
enumeration = root.depthFirstEnumeration();
seek();
}
private void seek() {
next = null;
CssTreeNode node;
while (enumeration.hasMoreElements()) {
node = (CssTreeNode) enumeration.nextElement();
if (node.isLeaf()) {
if (!node.isRoot()) {
next = node;
}
break;
}
}
}
public boolean hasNext() {
return next != null;
}
public CssTreeNode next() {
if (next == null) {
throw new NoSuchElementException("No more leafs");
}
CssTreeNode ret = next;
seek();
return ret;
}
public void remove() {
throw new UnsupportedOperationException("Not supported");
}
}
}
}