com.link_intersystems.graph.GraphFacade Maven / Gradle / Ivy
Show all versions of lis-commons-graph Show documentation
/**
* Copyright 2011 Link Intersystems GmbH
*
* 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.link_intersystems.graph;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static java.util.Arrays.asList;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterators.spliteratorUnknownSize;
import static java.util.stream.StreamSupport.stream;
/**
* Facade to ease the use of components within the graph package.
*
* @author René Link
* [rene.link@link-
* intersystems.com]
* @since 1.2.0;
*/
public abstract class GraphFacade {
/**
* Traverses the graph using the breadth first traversal strategy. Starting at
* the given {@link Node} and applies the given {@link Consumer} on every
* {@link Node} .
*
* @param start the {@link Node} to start the traversal from.
* @param nodeProcessor the closure that should be applied to every node.
* @since 1.2.0;
*/
public static void traverseBreadthFirst(Node start, Consumer nodeProcessor) {
Iterator objectGraphIterator = new BreadthFirstNodeIterator(start);
forAllDo(objectGraphIterator, nodeProcessor);
}
/**
* Traverses the graph using the depth first traversal strategy. Starting at the
* given {@link Node} and applies the given {@link Consumer} on every
* {@link Node} .
*
* @param start the {@link Node} to start the traversal from.
* @param nodeProcessor the closure that should be applied to every node.
* @since 1.0.0;
*/
public static void traverseDepthFirst(Node start, Consumer nodeProcessor) {
DepthFirstNodeIterator objectGraphTransformer = new DepthFirstNodeIterator(start);
forAllDo(objectGraphTransformer, nodeProcessor);
}
static void forAllDo(Iterator iterator, Consumer closure) {
while (iterator.hasNext()) {
T next = iterator.next();
closure.accept(next);
}
}
public enum NodeIterateStrategy {
DEPTH_FIRST {
@Override
public Iterator iterator(Node startNode) {
return new DepthFirstNodeIterator(startNode);
}
}, BREADTH_FIRST {
@Override
public Iterator iterator(Node startNode) {
return new BreadthFirstNodeIterator(startNode);
}
};
public abstract Iterator iterator(Node startNode);
}
/**
* Creates a predicated {@link Node} iterator that iterates the {@link Node} s
* per Predicate that is specified using the {@link NodeIterateStrategy}.
*
* Take the following node structure for example
*
*
* A
* +------------+------------+
* B C D
* +----+----+ | +---+------------+
* E F G H I J K
* +------+------+
* L M N
*
*
* If we assume that we have 3 {@link Predicate}s
*
* - The 1. {@link Predicate} matches {@link Node}s A,C,H,D,K
* - The 2. {@link Predicate} matches {@link Node}s A,B,E,F
* - The 3. {@link Predicate} matches {@link Node}s D,J,M,N
*
* and we construct a per predicated node iterator using
*
*
* {@link GraphFacade#perPredicateNodeIterator(NodeIterateStrategy, Node, Predicate...) GraphFacade.perPredicatedNodeIterator(BREADTH_FIRST, startNodeA, pred1, pred2, pred3)};
*
*
* The resulting iterator will iterate the node structure using a breadth first
* strategy for every {@link Predicate} starting at startNodeA.
* The result will be:
*
*
* breadth first breadth first breadth first
* matching matching matching
* pred1 pred2 pred3
* +-----------+ +-------+ +-------+
* A C D H K A B E F D J M N
*
* iterate order -->
*
*
*
*
* @param nodeIterateStrategy
* @param startNode
* @param nodeIterateOrderPredicates
* @return
*/
@SuppressWarnings("unchecked")
public static Iterator perPredicateNodeIterator(NodeIterateStrategy nodeIterateStrategy, Node startNode, Predicate... nodeIterateOrderPredicates) {
class FilteredIterator implements Iterator {
private final Iterator filtered;
public FilteredIterator(Iterator iterator, Predicate filter) {
filtered = stream(spliteratorUnknownSize(iterator, ORDERED), false).filter(filter).iterator();
}
@Override
public boolean hasNext() {
return filtered.hasNext();
}
@Override
public E next() {
return filtered.next();
}
}
class ChainedIterator implements Iterator {
private Iterator> chain;
private Iterator current = Collections.emptyIterator();
@SafeVarargs
public ChainedIterator(Iterator... iterators) {
this(asList(iterators));
}
public ChainedIterator(Collection> iterators) {
chain = iterators.iterator();
}
@Override
public boolean hasNext() {
while (!current.hasNext() && chain.hasNext()) {
current = chain.next();
}
return current.hasNext();
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return current.next();
}
}
List> iterators = new ArrayList<>();
for (int i = 0; i < nodeIterateOrderPredicates.length; i++) {
Predicate predicate = nodeIterateOrderPredicates[i];
Iterator nodeStrategyIterator = nodeIterateStrategy.iterator(startNode);
Iterator predicateFilterIterator = new FilteredIterator<>(nodeStrategyIterator, predicate);
iterators.add(predicateFilterIterator);
}
return new ChainedIterator<>(iterators);
}
}