/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.lanternpowered.server.util.graph;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
public class DirectedGraph<D> {
protected final List<DataNode<D>> nodes = Lists.newArrayList();
public DirectedGraph() {
}
public int getNodeCount() {
return this.nodes.size();
}
public int getEdgeCount() {
int count = 0;
for (DataNode<D> n : this.nodes) {
count += n.getEdgeCount();
}
return count;
}
public boolean contains(D data) {
for (DataNode<D> node : this.nodes) {
if (node.data.equals(data)) {
return true;
}
}
return false;
}
public DataNode<D> getNode(D data) {
for (DataNode<D> node : this.nodes) {
if (node.data.equals(data)) {
return node;
}
}
return null;
}
public void addEdge(D from, D to) {
add(from);
add(to);
DataNode<D> fromNode = getNode(from);
DataNode<D> toNode = getNode(to);
addEdge(fromNode, toNode);
}
public void addEdge(DataNode<D> from, DataNode<D> to) {
if (!this.nodes.contains(from)) {
this.nodes.add(from);
}
if (!this.nodes.contains(to)) {
this.nodes.add(to);
}
if (!from.isAdjacent(to)) {
from.addEdge(to);
}
}
public Iterable<DataNode<D>> getNodes() {
return this.nodes;
}
public DirectedGraph<D> reverse() {
DirectedGraph<D> rev = new DirectedGraph<>();
Map<DataNode<D>, DataNode<D>> siblings = Maps.newHashMap();
for (DataNode<D> n : this.nodes) {
DataNode<D> b = n.clone();
siblings.put(n, b);
}
for (DataNode<D> n : this.nodes) {
for (DataNode<D> b : n.getAdjacent()) {
rev.addEdge(siblings.get(b), siblings.get(n));
}
}
return rev;
}
public DataNode<D> add(D d) {
if (!contains(d)) {
final DataNode<D> node = new DataNode<>(d);
this.nodes.add(node);
}
return getNode(d);
}
public void add(DataNode<D> n) {
if (!contains(n.data)) {
this.nodes.add(n);
}
}
public void delete(D n) {
DataNode<D> node = null;
for (DataNode<D> node1 : this.nodes) {
if (node1.data.equals(n)) {
node = node1;
break;
}
}
if (node != null) {
delete(node);
}
}
public void delete(DataNode<D> n) {
for (DataNode<D> b : this.nodes) {
b.deleteEdge(n);
}
this.nodes.remove(n);
}
@Override
public String toString() {
String s = getNodeCount() + "\n" + getEdgeCount() + "\n";
for (DataNode<D> n : this.nodes) {
s += ((DataNode<?>) n).getData().toString() + " ";
for (DataNode<D> a : n.getAdjacent()) {
s += this.nodes.indexOf(a) + " ";
}
s += "\n";
}
return s;
}
public static class DataNode<D> {
private final List<DataNode<D>> adj = Lists.newArrayList();
private final D data;
public DataNode(D obj) {
this.data = obj;
}
public D getData() {
return this.data;
}
public void addEdge(DataNode<D> other) {
this.adj.add(other);
}
public void deleteEdge(DataNode<D> other) {
this.adj.remove(other);
}
public boolean isAdjacent(DataNode<D> other) {
return this.adj.contains(other);
}
public int getEdgeCount() {
return this.adj.size();
}
public Iterable<DataNode<D>> getAdjacent() {
return this.adj;
}
@Override
public DataNode<D> clone() {
return new DataNode<>(this.data);
}
@Override
public int hashCode() {
return this.data.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof DataNode)) {
return false;
}
DataNode<?> d = (DataNode<?>) o;
return d.getData().equals(this.data);
}
}
}