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.Map; import java.util.Stack; import java.util.TreeMap; import org.apache.commons.lang.StringUtils; import com.github.fritaly.graphml4j.GraphMLWriter; import com.github.fritaly.graphml4j.NodeStyle; /** * <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 GradleDependencies { /** * Computes and returns a label from the given value. * * @param value * a string identifying a dependency (group, artifact, version) * generated by the Gradle dependencies task. Can't be null. * @return a string corresponding to a shorter label to uniquely identify a * dependency. */ private static String computeLabel(String value) { // Examples of input values: // "org.springframework:spring-core:3.2.0.RELEASE (*)" => "spring-core:3.2.0.RELEASE" // "com.acme:acme-logging:1.16.0 -> 1.16.3 (*)" => "acme-logging:1.16.3" // "junit:junit:3.8.1 -> 4.8.1" => "junit:4.8.1" // "sun-jaxb:jaxb-impl:2.2" => "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; } // Ex: "sun-jaxb:jaxb-impl:2.2" => "jaxb-impl:2.2" value = StringUtils.substringAfter(value, ":"); return StringUtils.replace(value, ":", "\n"); } public static void main(String[] args) throws Exception { if (args.length != 1) { System.out.println(String.format("%s <output-file>", GradleDependencies.class.getSimpleName())); System.exit(1); } final File file = new File(args[0]); System.out.println("Writing GraphML file to " + file.getAbsolutePath() + " ..."); FileWriter fileWriter = null; GraphMLWriter graphWriter = null; Reader reader = null; LineNumberReader lineReader = null; try { fileWriter = new FileWriter(file); graphWriter = new GraphMLWriter(fileWriter); // Customize the rendering of nodes final NodeStyle nodeStyle = graphWriter.getNodeStyle(); nodeStyle.setWidth(250.0f); graphWriter.setNodeStyle(nodeStyle); // 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(GradleDependencies.class.getResourceAsStream("gradle-dependencies.txt")); lineReader = new LineNumberReader(reader); String line = null; // Stack containing the node identifiers per depth inside the // dependency graph (the topmost dependency is the first one in the // stack) final Stack<String> parentIds = new Stack<String>(); // Open the graph graphWriter.graph(); // Map storing the node identifiers per label final Map<String, String> nodeIdsByLabel = new TreeMap<String, String>(); 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 <= parentIds.size()) { parentIds.pop(); } // Compute a nice label from the dependency (group, artifact, // version) tuple final String label = computeLabel(line); // Has this dependency already been added to the graph ? if (!nodeIdsByLabel.containsKey(label)) { // No, add the node nodeIdsByLabel.put(label, graphWriter.node(label)); } final String nodeId = nodeIdsByLabel.get(label); parentIds.push(nodeId); if (parentIds.size() > 1) { // Generate an edge between the current node and its parent graphWriter.edge(parentIds.get(parentIds.size() - 2), nodeId); } } // Close the graph graphWriter.closeGraph(); System.out.println("Done"); } finally { // Calling GraphMLWriter.close() is necessary to dispose the underlying resources graphWriter.close(); fileWriter.close(); lineReader.close(); reader.close(); } } }