cdc.graphs.core.GraphPath Maven / Gradle / Ivy
package cdc.graphs.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* A Path is an ordered list of items (typically nodes or edges).
*
* @author Damien Carbonne
*
* @param The item class.
*/
public final class GraphPath {
private final List items;
private static final Comparator> DESCENDING_SIZE_COMPARATOR =
(p1,
p2) -> p2.size() - p1.size();
private GraphPath() {
this.items = Collections.emptyList();
}
private GraphPath(List items) {
this.items = Collections.unmodifiableList(new ArrayList<>(items));
}
/**
* @return The list of items of this path.
*/
public List getItems() {
return items;
}
/**
* @return The number of items in the path.
*/
public int size() {
return items.size();
}
/**
* @return True if this path is empty.
*/
public boolean isEmpty() {
return items.isEmpty();
}
/**
* Returns true when an item is contained in this path.
*
* @param item The item.
* @return True when this path contains item.
*/
public boolean contains(T item) {
return items.contains(item);
}
/**
* @return The parent path.
*/
public GraphPath parent() {
if (size() <= 1) {
return new GraphPath<>();
} else {
return new GraphPath<>(items.subList(0, items.size() - 1));
}
}
/**
* Returns true when this path contains another path.
*
* @param other The other path.
* @return True when other is a sub-path of this path.
*/
public boolean includes(GraphPath other) {
if (other.size() > size()) {
return false;
} else {
final int delta = size() - other.size();
for (int d = 0; d <= delta; d++) {
boolean match = true;
for (int index = 0; index < other.size(); index++) {
if (items.get(index + d) != other.items.get(index)) {
match = false;
break;
}
}
if (match) {
return true;
}
}
return false;
}
}
/**
* @param other The other path.
* @return {@code true} if this path and {@code other} path have common items.
*/
public boolean hasCommonItemsWith(GraphPath other) {
if (items.size() <= other.items.size()) {
final Set set = new HashSet<>(other.items);
for (final T item : items) {
if (set.contains(item)) {
return true;
}
}
return false;
} else {
final Set set = new HashSet<>(items);
for (final T item : other.items) {
if (set.contains(item)) {
return true;
}
}
return false;
}
}
/**
* Reduces a set of paths so that no path in the set is a sub-path of another path.
*
* @param The item class.
* @param paths The set of paths.
*/
public static void reduce(Set> paths) {
// When there are too few paths, nothing needs t be done
if (paths.size() <= 1) {
return;
}
// There are at least 2 paths
// Add paths to tmp in descending size
final List> tmp = new ArrayList<>(paths);
Collections.sort(tmp, DESCENDING_SIZE_COMPARATOR);
// Resets paths set
// It will be filled with appropriate paths
paths.clear();
// Test paths from longer ones to smaller ones
for (int index = 0; index < tmp.size(); index++) {
// Next path to test
final GraphPath next = tmp.get(index);
// Check whether next is included in one of the paths contained in
// paths or not.
boolean included = false;
for (final GraphPath path : paths) {
if (path.includes(next)) {
included = true;
break;
}
}
if (!included) {
paths.add(next);
}
}
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof GraphPath)) {
return false;
}
final GraphPath> o = (GraphPath>) other;
return items.equals(o.items);
}
@Override
public int hashCode() {
return items.hashCode();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[");
boolean first = true;
for (final T item : items) {
if (!first) {
builder.append(" ");
}
builder.append(item);
first = false;
}
builder.append("]");
return builder.toString();
}
public static Builder builder() {
return new Builder<>();
}
public static Builder builder(final Class cls) {
return new Builder<>();
}
public static final class Builder {
private final List items = new ArrayList<>();
private Builder() {
}
/**
* Clears this path builder.
*
* @return This Builder.
*/
public Builder clear() {
items.clear();
return this;
}
/**
* Adds an item at the end of path.
*
* @param item the item.
* @return This Builder.
*/
public Builder push(T item) {
items.add(item);
return this;
}
/**
* Adds items at the end of path.
*
* @param items The items.
* @return This Builder.
*/
@SuppressWarnings("unchecked")
public Builder push(T... items) {
Collections.addAll(this.items, items);
return this;
}
public Builder push(List items) {
this.items.addAll(items);
return this;
}
public Builder push(GraphPath path) {
items.addAll(path.getItems());
return this;
}
/**
* Removes the last item of the path.
*
* @return This Builder.
*/
public Builder pop() {
items.remove(items.size() - 1);
return this;
}
public List getItems() {
return items;
}
public boolean contains(T item) {
return items.contains(item);
}
public GraphPath build() {
return new GraphPath<>(items);
}
}
}