Skip to content

Commit

Permalink
moditect#27 Making Node a self-referential interface type, allowing f…
Browse files Browse the repository at this point in the history
…or specific sub-types
  • Loading branch information
gunnarmorling committed Jan 27, 2019
1 parent 9a69924 commit 2e21d50
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 166 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright 2019 The ModiTect authors
*
* 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 org.moditect.deptective.internal.graph;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
* A cycle between multiple nodes.
*
* @author Gunnar Morling
* @param <T> the specific node type
*/
public class Cycle<T extends Node<T>> {

private static final String STRING_DELIMITER = ", ";
private final List<T> nodes;

public Cycle(List<T> nodes) {
this.nodes = Collections.unmodifiableList(nodes);
}

public List<T> getNodes() {
return nodes;
}

@Override
public String toString() {
return nodes.stream()
.map(Node::asShortString)
.sorted()
.collect(Collectors.joining(STRING_DELIMITER));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
/**
* @author Gerd W&uuml;therich ([email protected])
*/
public class Dependency {
public class Dependency<T extends Node<T>> {

private final Node to;
private final T to;
private final int aggregatedWeight;

public Dependency(Node to, int aggregatedWeight) {
public Dependency(T to, int aggregatedWeight) {
this.to = to;
this.aggregatedWeight = aggregatedWeight;
}

public Node getTo() {
public T getTo() {
return to;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package org.moditect.deptective.internal.graph;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -42,8 +41,8 @@ public class GraphUtils {
* contain just a single node. If you want to detect 'real' cycle (size > 1) please use
* {@link GraphUtils#detectCycles(Collection)}.
*/
public static List<List<Node>> detectStronglyConnectedComponents(Collection<Node> nodes) {
return new Tarjan<>().detectStronglyConnectedComponents(Objects.requireNonNull(nodes));
public static <T extends Node<T>> List<List<T>> detectStronglyConnectedComponents(Collection<T> nodes) {
return new Tarjan<T>().detectStronglyConnectedComponents(Objects.requireNonNull(nodes));
}

/**
Expand All @@ -52,8 +51,11 @@ public static List<List<Node>> detectStronglyConnectedComponents(Collection<Node
* @param nodes
* @return a list of strongly connected components (SCCs) with a size > 1.
*/
public static List<List<Node>> detectCycles(Collection<Node> nodes) {
return new Tarjan<>().detectStronglyConnectedComponents(nodes).stream().filter(cycle -> cycle.size() > 1)
public static <T extends Node<T>> List<Cycle<T>> detectCycles(Iterable<T> nodes) {
return new Tarjan<T>().detectStronglyConnectedComponents(nodes)
.stream()
.filter(cycle -> cycle.size() > 1)
.map(Cycle::new)
.collect(Collectors.toList());
}

Expand All @@ -63,9 +65,9 @@ public static List<List<Node>> detectCycles(Collection<Node> nodes) {
* @param nodes the collection of nodes
* @return
*/
public static IDependencyStructureMatrix createDependencyStructureMatrix(
Collection<Node> nodes) {
return new DependencyStructureMatrix(nodes);
public static <T extends Node<T>> IDependencyStructureMatrix<T> createDependencyStructureMatrix(
Collection<T> nodes) {
return new DependencyStructureMatrix<T>(nodes);
}

/**
Expand All @@ -75,23 +77,12 @@ public static IDependencyStructureMatrix createDependencyStructureMatrix(
* @param nodes the collection of nodes
* @return the adjacency matrix for the given list of nodes
*/
public static int[][] computeAdjacencyMatrix(List<Node> nodes) {
public static <T extends Node<T>> int[][] computeAdjacencyMatrix(List<T> nodes) {
Objects.requireNonNull(nodes);
return computeAdjacencyMatrix(nodes.toArray(new Node[nodes.size()]));
}

/**
* An adjacency matrix is a square matrix used to represent a finite graph. The elements of the matrix
* indicate whether pairs of vertices are connected (adjacent) or not in the graph.
*
* @param nodes the array of nodes
* @return the adjacency matrix for the given list of nodes
*/
public static int[][] computeAdjacencyMatrix(Node... nodes) {
int[][] result = new int[nodes.length][nodes.length];
int[][] result = new int[nodes.size()][nodes.size()];
for (int i = 0; i < result.length; i++) {
for (int j = 0; j < result.length; j++) {
Dependency dependency = nodes[i].getOutgoingDependencyTo(nodes[j]);
Dependency<T> dependency = nodes.get(i).getOutgoingDependencyTo(nodes.get(j));
result[i][j] = dependency != null ? dependency.getAggregatedWeight() : 0;
}
}
Expand All @@ -105,40 +96,28 @@ public static int[][] computeAdjacencyMatrix(Node... nodes) {
* @param nodes the array of nodes
* @return the adjacency list for the given list of nodes
*/
public static int[][] computeAdjacencyList(Collection<Node> nodes) {
Objects.requireNonNull(nodes);
return computeAdjacencyList(nodes.toArray(new Node[nodes.size()]));
}

/**
* An adjacency list is a collection of (unordered) lists used to represent a finite graph. Each list
* describes the set of neighbors of a node.
*
* @param nodes the array of nodes
* @return the adjacency list for the given list of nodes
*/
public static int[][] computeAdjacencyList(Node... nodes) {
public static <T extends Node<T>> int[][] computeAdjacencyList(Iterable<T> nodes) {

int[][] matrix;

// prepare
int i = 0;
Map<Node, Integer> map = new HashMap<Node, Integer>();
for (Node iArtifact : nodes) {
Map<T, Integer> map = new HashMap<T, Integer>();
for (T iArtifact : nodes) {
map.put(iArtifact, i);
i++;
}
matrix = new int[nodes.length][];
matrix = new int[map.size()][];

for (Node node : nodes) {
Collection<Dependency> dependencies = node.getOutgoingDependenciesTo(Arrays.asList(nodes));
for (T node : nodes) {
Collection<Dependency<T>> dependencies = node.getOutgoingDependenciesTo(nodes);
if (dependencies == null) {
dependencies = Collections.emptyList();
}
int index = map.get(node);
matrix[index] = new int[dependencies.size()];
int count = 0;
for (Dependency dependency : dependencies) {
for (Dependency<?> dependency : dependencies) {
matrix[index][count] = map.get(dependency.getTo());
count++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
/**
* @author Gerd W&uuml;therich ([email protected])
*/
public interface IDependencyStructureMatrix {
public interface IDependencyStructureMatrix<T extends Node<T>> {

List<Node> getOrderedNodes();
List<T> getOrderedNodes();

List<Dependency> getUpwardDependencies();
List<Dependency<T>> getUpwardDependencies();

List<List<Node>> getCycles();
List<List<T>> getCycles();

boolean isCellInCycle(int i, int j);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
*/
public interface INodeSorter {

SortResult sort(List<Node> node);
<T extends Node<T>> SortResult<T> sort(List<T> node);

public interface SortResult {
public interface SortResult<T extends Node<T>> {

List<Node> getOrderedNodes();
List<T> getOrderedNodes();

List<Dependency> getUpwardsDependencies();
List<Dependency<T>> getUpwardsDependencies();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,87 +15,35 @@
*/
package org.moditect.deptective.internal.graph;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
* A node in a graph.
*
* @author Gerd W&uuml;therich ([email protected])
* @author Gunnar Morling
* @param <T> A specific sub-type, following the self-referential generic type pattern
*/
public class Node {

private Map<Node, Dependency> outgoingDependencies;
private final String id;

public Node(String id) {
this.id = Objects.requireNonNull(id);
}

public String getId() {
return id;
}

public Dependency getOutgoingDependencyTo(Node node) {
if (!hasOutgoingDependencies()) {
return null;
}

return outgoingDependencies.get(node);
}
public interface Node<T extends Node<T>> {

public Set<Dependency> getOutgoingDependenciesTo(Collection<Node> nodes) {
return Objects.requireNonNull(nodes).stream()
.map(node -> getOutgoingDependencyTo(node))
.filter(dep -> dep != null)
default Set<Dependency<T>> getOutgoingDependenciesTo(Iterable<T> nodes) {
return StreamSupport.stream(nodes.spliterator(), false)
.map(c -> getOutgoingDependencyTo(c))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}

public boolean hasOutgoingDependencies() {
return outgoingDependencies != null && !outgoingDependencies.isEmpty();
}

public void addOutgoingDependency(Node to, int aggregatedWeight) {
outgoingDependencies().put(to, new Dependency(to, aggregatedWeight));
}

@Override
public String toString() {
return "Node [id=" + id + "]";
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
Dependency<T> getOutgoingDependencyTo(T node);

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (id == null) {
if (other.id != null)
return false;
}
else if (!id.equals(other.id))
return false;
return true;
}
boolean hasOutgoingDependencies();

private Map<Node, Dependency> outgoingDependencies() {
if (outgoingDependencies == null) {
outgoingDependencies = new HashMap<>();
}
return outgoingDependencies;
/**
* Returns a concise string representation of this node, e.g. used when rendering this node in a diagram.
*/
default String asShortString() {
return toString();
}
}
Loading

0 comments on commit 2e21d50

Please sign in to comment.