Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.github.fritaly.graphml4j.samples; import java.io.File; import java.io.FileWriter; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.Reader; import java.util.Stack; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.HashCodeBuilder; import com.github.fritaly.graphml4j.EdgeStyle; import com.github.fritaly.graphml4j.GroupStyles; import com.github.fritaly.graphml4j.NodeStyle; import com.github.fritaly.graphml4j.Renderer; import com.github.fritaly.graphml4j.datastructure.Edge; import com.github.fritaly.graphml4j.datastructure.Graph; import com.github.fritaly.graphml4j.datastructure.Node; /** * <p> * This sample demonstrates how to generate a dependency graph (to be visualized * in yEd) from the output of a "gradle dependencies" command. The Gradle output * has been saved as a resource file (see resource "gradle-dependencies.txt"). * </p> * <p> * Instructions: * <ul> * <li>Download and install yEd (if necessary)</li> * <li>Execute this sample and generate a GraphML file</li> * <li>Open the generated file in yEd</li> * <li>In yEd, render the graph with the "Hierarchical" layout</li> * </ul> * </p> * * @author francois_ritaly */ public class GradleDependenciesWithGroupsAndBuffering { private static class Artifact { private final String group, artifact, version; public Artifact(String value) { // Ex: "junit:junit:4.8.1" this.group = StringUtils.substringBefore(value, ":"); this.artifact = StringUtils.substringBetween(value, ":"); this.version = StringUtils.substringAfterLast(value, ":"); } String getLabel() { return String.format("%s\n%s", artifact, version); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj instanceof Artifact) { final Artifact other = (Artifact) obj; return StringUtils.equals(this.group, other.group) && StringUtils.equals(this.artifact, other.artifact) && StringUtils.equals(this.version, other.version); } return false; } @Override public int hashCode() { return new HashCodeBuilder(7, 23).append(this.group).append(this.artifact).append(this.version) .toHashCode(); } @Override public String toString() { return String.format("%s:%s:%s", group, artifact, version); } } private static Artifact createArtifact(String value) { // Examples of input values: // "org.springframework:spring-core:3.2.0.RELEASE (*)" // "com.acme:acme-logging:1.16.0 -> 1.16.3 (*)" // "junit:junit:3.8.1 -> 4.8.1" // "sun-jaxb:jaxb-impl:2.2" if (value.endsWith(" (*)")) { // Ex: "org.springframework:spring-core:3.2.0.RELEASE (*)" => "org.springframework:spring-core:3.2.0.RELEASE" value = value.replace(" (*)", ""); } if (value.contains(" -> ")) { // Ex: "junit:junit:3.8.1 -> 4.8.1" => "junit:junit:4.8.1" final String version = StringUtils.substringAfter(value, " -> "); value = StringUtils.substringBeforeLast(value, ":") + ":" + version; } return new Artifact(value); } public static void main(String[] args) throws Exception { if (args.length != 1) { System.out.println(String.format("%s <output-file>", GradleDependenciesWithGroupsAndBuffering.class.getSimpleName())); System.exit(1); } final File file = new File(args[0]); System.out.println("Writing GraphML file to " + file.getAbsolutePath() + " ..."); FileWriter fileWriter = null; Reader reader = null; LineNumberReader lineReader = null; try { fileWriter = new FileWriter(file); final com.github.fritaly.graphml4j.datastructure.Graph graph = new Graph(); // The dependency graph has been generated by Gradle with the // command "gradle dependencies". The output of this command has // been saved to a text file which will be parsed to rebuild the // dependency graph reader = new InputStreamReader( GradleDependenciesWithGroupsAndBuffering.class.getResourceAsStream("gradle-dependencies.txt")); lineReader = new LineNumberReader(reader); String line = null; // Stack containing the nodes per depth inside the dependency graph // (the topmost dependency is the first one in the stack) final Stack<Node> parentNodes = new Stack<Node>(); while ((line = lineReader.readLine()) != null) { // Determine the depth of the current dependency inside the // graph. The depth can be inferred from the indentation used by // Gradle. Each level of depth adds 5 more characters of // indentation final int initialLength = line.length(); // Remove the strings used by Gradle to indent dependencies line = StringUtils.replace(line, "+--- ", ""); line = StringUtils.replace(line, "| ", ""); line = StringUtils.replace(line, "\\--- ", ""); line = StringUtils.replace(line, " ", ""); // The depth can easily be inferred now final int depth = (initialLength - line.length()) / 5; // Remove unnecessary node ids while (depth <= parentNodes.size()) { parentNodes.pop(); } final Artifact artifact = createArtifact(line); Node node = graph.getNodeByData(artifact); // Has this dependency already been added to the graph ? if (node == null) { // No, add the node node = graph.addNode(artifact); } parentNodes.push(node); if (parentNodes.size() > 1) { // Generate an edge between the current node and its parent graph.addEdge("Depends on", parentNodes.get(parentNodes.size() - 2), node); } } // Create the groups after creating the nodes & edges for (Node node : graph.getNodes()) { final Artifact artifact = (Artifact) node.getData(); final String groupId = artifact.group; Node groupNode = graph.getNodeByData(groupId); if (groupNode == null) { groupNode = graph.addNode(groupId); } // add the node to the group node.setParent(groupNode); } graph.toGraphML(fileWriter, new Renderer() { @Override public String getNodeLabel(Node node) { return node.isGroup() ? node.getData().toString() : ((Artifact) node.getData()).getLabel(); } @Override public boolean isGroupOpen(Node node) { return true; } @Override public NodeStyle getNodeStyle(Node node) { // Customize the rendering of nodes final NodeStyle nodeStyle = new NodeStyle(); nodeStyle.setWidth(250.0f); return nodeStyle; } @Override public GroupStyles getGroupStyles(Node node) { return new GroupStyles(); } @Override public EdgeStyle getEdgeStyle(Edge edge) { return new EdgeStyle(); } }); System.out.println("Done"); } finally { // Calling GraphMLWriter.close() is necessary to dispose the underlying resources fileWriter.close(); lineReader.close(); reader.close(); } } }