All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.lightblue.util.AbstractTreeCursor Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 Copyright 2013 Red Hat, Inc. and/or its affiliates.

 This file is part of lightblue.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see .
 */
package com.redhat.lightblue.util;

import java.util.LinkedList;

/**
 * An abstract cursor for traversing a tree of name-value pairs. The class
 * parameter N denotes the node type of the underlying tree. For instance, a
 * Json document could be traversed using a concrete implementation extending
 * AbstractTreeCursor.
 *
 * Every node in the tree is identified by two components: the path to the node,
 * and the actual node object itself. The cursor provides methods to seek to the
 * first child and the next sibling of the current node, as well as the parent
 * node of the current node. The cursor cannot move to the next sibling or to
 * the parent of the root node with which it is initialized.
 *
 * For example:
 * 
 *
 *  |
 *  +-A
 *  |
 *  +-B
 *  | |
 *  | +-C
 *  | |
 *  | +-D
 *  |   |
 *  |   +-E
 *  +-F
 * 
Assuming the cursor is initialized with the root node of the above * tree: *
 *    cursor.getCurrentNode() => Docroot
 *    cursor.getCurrentPath() => ""
 * 
After cursor.getFirstChild(): *
 *    cursor.getCurrentNode() => A
 *    cursor.getCurrentPath() => "A"
 * 
At this point, cursor.getFirstChild() will return null. * cursor.getNextSibling() will seek to node B. After calling * cursor.getFirstChild(): *
 *     cursor.getCurrentNode() => C
 *     cursor.getCurrentPath() => "B.C"
 * 
* * The concrete implementation must provide two methods to provide access to the * underlying tree structure: *
    *
  • hasChildren: Return if the node has children
  • *
  • getCursor: If the node has children, return a KeyValueCursor to iterate * the elements of the node. In the above example, a getCursor call * with node B as the argument should return a cursor that will return C and * D.
  • *
*/ public abstract class AbstractTreeCursor { /** * Iteration state for every level is kept in a stack. */ private final LinkedList> stack = new LinkedList<>(); /** * Current absolute path in tree */ private final MutablePath currentPath; /** * Current node in tree. Never null */ private N currentNode; /** * Current cursor we're using to iterate the level. Never null. */ private KeyValueCursor currentCursor; private static final class SingleElemCursor implements KeyValueCursor { private T value; private final String key; private boolean fetched = false; SingleElemCursor(String key, T value) { this.key = key; this.value = value; } @Override public boolean hasNext() { return !fetched; } @Override public void next() { fetched = true; } @Override public T getCurrentValue() { return value; } @Override public String getCurrentKey() { return key; } } /** * Keeps the node and the cursor for the level */ private static final class LevelState { private final T node; private final KeyValueCursor cursor; public LevelState(T node, KeyValueCursor cursor) { this.node = node; this.cursor = cursor; } public String toString() { return node.toString(); } } /** * Construct a cursor at a given location in a tree * * @param p Path to the root node for the cursor. If docroot, empty path. * @param start The root node */ public AbstractTreeCursor(Path p, N start) { currentPath = new MutablePath(p); currentNode = start; if (!hasChildren(start)) { throw new IllegalArgumentException("Not iterable"); } currentCursor = new SingleElemCursor("", start); currentCursor.next(); } /** * Get the current node the cursor is pointing to */ public N getCurrentNode() { return currentNode; } /** * Get the current path the cursor is pointing to */ public Path getCurrentPath() { return currentPath.immutableCopy(); } /** * Attempt to seek the cursor to the first child of the current node * * @return If the current node does not have any children, returns false, * and cursor still points to the node it was pointing to before the call. * If the current node has children, cursor points to the first child of the * node, and true is returned. */ public boolean firstChild() { // push current state to stack, and get the first child of TOS if (hasChildren(currentNode)) { push(); currentCursor = getCursor(currentNode); if (!currentCursor.hasNext()) { pop(); return false; } currentCursor.next(); currentNode = currentCursor.getCurrentValue(); currentPath.push(currentCursor.getCurrentKey()); return true; } else { return false; } } /** * Attempt to seek the cursor to the next sibling of the current node * * @return If the current node is the last node of its parent, returns * false, and the cursor still points to the same node. Otherwise, cursor * points to the next sibling and true is returned. */ public boolean nextSibling() { if (currentCursor.hasNext()) { currentCursor.next(); currentNode = currentCursor.getCurrentValue(); currentPath.setLast(currentCursor.getCurrentKey()); return true; } else { return false; } } /** * Attempt to seek the cursor to the parent of the current node * * @return If the node has a parent that is a descendent of the root node * with which the cursor is initialized, seeks to the parent and returns * true. Otherwise, returns false and cursor still points to the same node. */ public boolean parent() { if (!stack.isEmpty()) { pop(); currentPath.pop(); return true; } else { return false; } } /** * Attempts to seek to the next node in a depth-first manner * * @return If there is a next node in a depth-first manner, returns true and * seeks to that node. Otherwise, returns false and the cursor continues to * point to the same node. */ public boolean next() { boolean done = false; boolean backing = false; do { if (backing) { if (nextSibling()) { return true; } else if (!parent()) { done = true; } } else if (firstChild() || nextSibling()) { return true; } else if (!parent()) { done = true; } else { backing = true; } } while (!done); return false; } /** * Returns a cursor over the immediate children of the node * * @param node The node for which the cursor will be returned */ protected abstract KeyValueCursor getCursor(N node); /** * Returns true if the node given node has children */ protected abstract boolean hasChildren(N node); private void push() { stack.addLast(new LevelState<>(currentNode, currentCursor)); } private void pop() { LevelState last = stack.removeLast(); currentNode = last.node; currentCursor = last.cursor; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy