diff --git a/api/server/src/main/java/org/softwareheritage/graph/App.java b/api/server/src/main/java/org/softwareheritage/graph/App.java index cdcbb28..13e8be4 100644 --- a/api/server/src/main/java/org/softwareheritage/graph/App.java +++ b/api/server/src/main/java/org/softwareheritage/graph/App.java @@ -1,46 +1,52 @@ package org.softwareheritage.graph; import java.io.IOException; import java.util.Optional; import io.javalin.Javalin; import org.softwareheritage.graph.Graph; +import org.softwareheritage.graph.SwhId; import org.softwareheritage.graph.algo.Stats; import org.softwareheritage.graph.algo.Visit; public class App { public static void main(String[] args) throws IOException, Exception { String path = args[0]; Graph graph = new Graph(path); Javalin app = Javalin.create().start(5010); app.get("/stats/:src_type/:dst_type", ctx -> { try { String srcType = ctx.pathParam("src_type"); String dstType = ctx.pathParam("dst_type"); ctx.json(new Stats(srcType, dstType)); } catch (IllegalArgumentException e) { ctx.status(404); } catch (Exception e) { ctx.status(400); ctx.result(e.toString()); } }); app.get("/visit/:swh_id", ctx -> { - String start = ctx.pathParam("swh_id"); + try { + SwhId start = new SwhId(ctx.pathParam("swh_id")); - // By default, traversal is a forward DFS using all edges - String algorithm = Optional.ofNullable(ctx.queryParam("traversal")).orElse("dfs"); - String direction = Optional.ofNullable(ctx.queryParam("direction")).orElse("forward"); - String edges = Optional.ofNullable(ctx.queryParam("edges")).orElse("cnt:dir:rel:rev:snp"); + // By default, traversal is a forward DFS using all edges + String algorithm = Optional.ofNullable(ctx.queryParam("traversal")).orElse("dfs"); + String direction = Optional.ofNullable(ctx.queryParam("direction")).orElse("forward"); + String edges = Optional.ofNullable(ctx.queryParam("edges")).orElse("cnt:dir:rel:rev:snp"); - // TODO: Use transposed graph depending on 'direction' - ctx.json(new Visit(graph, start, edges, algorithm)); + // TODO: Use transposed graph depending on 'direction' + ctx.json(new Visit(graph, start, edges, algorithm)); + } catch (IllegalArgumentException e) { + ctx.status(400); + ctx.result(e.toString()); + } }); app.error(404, ctx -> { ctx.result("Not found"); }); } } diff --git a/api/server/src/main/java/org/softwareheritage/graph/Graph.java b/api/server/src/main/java/org/softwareheritage/graph/Graph.java index cbbd03b..2f1a761 100644 --- a/api/server/src/main/java/org/softwareheritage/graph/Graph.java +++ b/api/server/src/main/java/org/softwareheritage/graph/Graph.java @@ -1,46 +1,47 @@ package org.softwareheritage.graph; import it.unimi.dsi.big.webgraph.BVGraph; import it.unimi.dsi.big.webgraph.LazyLongIterator; import org.softwareheritage.graph.NodeIdMap; +import org.softwareheritage.graph.SwhId; public class Graph { BVGraph graph; String path; NodeIdMap nodeIdMap; public Graph(String graphPath) throws Exception { this.graph = BVGraph.load(graphPath); this.path = graphPath; this.nodeIdMap = new NodeIdMap(graphPath); } public String getPath() { return path; } - public long getNode(String hash) { - return nodeIdMap.getNode(hash); + public long getNode(SwhId swhId) { + return nodeIdMap.getNode(swhId); } - public String getHash(long node) { - return nodeIdMap.getHash(node); + public SwhId getSwhId(long node) { + return nodeIdMap.getSwhId(node); } public long getNbNodes() { return graph.numNodes(); } public long getNbEdges() { return graph.numArcs(); } public LazyLongIterator successors(long node) { return graph.successors(node); } public long outdegree(long node) { return graph.outdegree(node); } } diff --git a/api/server/src/main/java/org/softwareheritage/graph/NodeIdMap.java b/api/server/src/main/java/org/softwareheritage/graph/NodeIdMap.java index 65884e5..d222a99 100644 --- a/api/server/src/main/java/org/softwareheritage/graph/NodeIdMap.java +++ b/api/server/src/main/java/org/softwareheritage/graph/NodeIdMap.java @@ -1,21 +1,23 @@ package org.softwareheritage.graph; +import org.softwareheritage.graph.SwhId; + // TODO: decide on how to do the disk-based node id map public class NodeIdMap { String graphPath; public NodeIdMap(String graphPath) { this.graphPath = graphPath; } - public long getNode(String hash) { + public long getNode(SwhId swhId) { return 42; } - public String getHash(long node) { + public SwhId getSwhId(long node) { return null; } public void dump() { } } diff --git a/api/server/src/main/java/org/softwareheritage/graph/SwhId.java b/api/server/src/main/java/org/softwareheritage/graph/SwhId.java new file mode 100644 index 0000000..375ff81 --- /dev/null +++ b/api/server/src/main/java/org/softwareheritage/graph/SwhId.java @@ -0,0 +1,31 @@ +package org.softwareheritage.graph; + +public class SwhId { + String swhId; + String type; + String hash; + + public SwhId(String swhId) { + this.swhId = swhId; + String[] parts = swhId.split(":"); + if (parts.length != 4) { + throw new IllegalArgumentException("Incorrect SWH ID format: " + swhId); + } + + // SWH ID format: 'swh:1:type:hash' + this.type = parts[2]; + this.hash = parts[3]; + } + + public String getType() { + return type; + } + + public String getHash() { + return hash; + } + + public String toString() { + return swhId; + } +} diff --git a/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java b/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java index 88c3db9..1b1fd49 100644 --- a/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java +++ b/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java @@ -1,65 +1,66 @@ package org.softwareheritage.graph.algo; import java.util.ArrayList; import java.util.Stack; import it.unimi.dsi.big.webgraph.LazyLongIterator; import it.unimi.dsi.bits.LongArrayBitVector; import org.softwareheritage.graph.Graph; +import org.softwareheritage.graph.SwhId; public class Visit { - public class Path extends ArrayList {} + public class Path extends ArrayList {} Graph graph; String allowedEdges; Stack currentPath; ArrayList paths; LongArrayBitVector visited; - public Visit(Graph graph, String start, String allowedEdges, String algorithm) { + public Visit(Graph graph, SwhId start, String allowedEdges, String algorithm) { this.graph = graph; this.allowedEdges = allowedEdges; this.paths = new ArrayList(); this.currentPath = new Stack(); this.visited = LongArrayBitVector.ofLength(graph.getNbNodes()); if (algorithm == "dfs") { dfs(graph.getNode(start)); } } // Allow Jackson JSON to only serialize the 'paths' field public ArrayList getPaths() { return paths; } private void dfs(long current) { visited.set(current); currentPath.push(current); long degree = graph.outdegree(current); if (degree == 0) { Path path = new Path(); for (long node : currentPath) { - path.add(graph.getHash(node)); + path.add(graph.getSwhId(node)); } paths.add(path); } LazyLongIterator successors = graph.successors(current); while (degree-- > 0) { long next = successors.nextLong(); if (isEdgeAllowed(current, next) && !visited.getBoolean(next)) { dfs(next); } } currentPath.pop(); } private boolean isEdgeAllowed(long current, long next) { // TODO return true; } }