Java tutorial
/** * Copyright 2015-2017 Linagora, Universit Joseph Fourier, Floralis * * The present code is developed in the scope of the joint LINAGORA - * Universit Joseph Fourier - Floralis research program and is designated * as a "Result" pursuant to the terms and conditions of the LINAGORA * - Universit Joseph Fourier - Floralis research program. Each copyright * holder of Results enumerated here above fully & independently holds complete * ownership of the complete Intellectual Property rights applicable to the whole * of said Results, and may freely exploit it in any manner which does not infringe * the moral rights of the other copyright holders. * * 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 net.roboconf.doc.generator.internal; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.JComponent; import org.apache.commons.collections15.Transformer; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.visualization.VisualizationImageServer; import edu.uci.ics.jung.visualization.decorators.AbstractEdgeShapeTransformer; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position; import net.roboconf.core.model.beans.AbstractType; import net.roboconf.core.model.beans.Component; import net.roboconf.core.utils.Utils; import net.roboconf.doc.generator.DocConstants; /** * @author Vincent Zurczak - Linagora */ public final class GraphUtils { public static final int SHAPE_HEIGHT = 80; /** * Private empty constructor. */ private GraphUtils() { // nothing } /** * Computes the width of a shape for a given component or facet. * @param type a type * @return the width it should take once displayed as a graph vertex */ public static int computeShapeWidth(AbstractType type) { Font font = GraphUtils.getDefaultFont(); BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); FontMetrics fm = img.getGraphics().getFontMetrics(font); int width = fm.stringWidth(type.getName()); width = Math.max(width, 80) + 20; return width; } /** * @return the default font to use in graph diagrams */ public static Font getDefaultFont() { return new Font("Helvetica Neue", Font.BOLD, 15); } /** * Writes a graph as a PNG image. * @param outputFile the output file * @param selectedComponent the component to highlight * @param layout the layout * @param graph the graph * @param edgeShapeTransformer the transformer for edge shapes (straight line, curved line, etc) * @throws IOException if something went wrong */ public static void writeGraph(File outputFile, Component selectedComponent, Layout<AbstractType, String> layout, Graph<AbstractType, String> graph, AbstractEdgeShapeTransformer<AbstractType, String> edgeShapeTransformer, Map<String, String> options) throws IOException { VisualizationImageServer<AbstractType, String> vis = new VisualizationImageServer<AbstractType, String>( layout, layout.getSize()); vis.setBackground(Color.WHITE); vis.getRenderContext().setEdgeLabelTransformer(new NoStringLabeller()); vis.getRenderContext().setEdgeShapeTransformer(edgeShapeTransformer); vis.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<AbstractType>()); vis.getRenderContext().setVertexShapeTransformer(new VertexShape()); vis.getRenderContext().setVertexFontTransformer(new VertexFont()); Color defaultBgColor = decode(options.get(DocConstants.OPTION_IMG_BACKGROUND_COLOR), DocConstants.DEFAULT_BACKGROUND_COLOR); Color highlightBgcolor = decode(options.get(DocConstants.OPTION_IMG_HIGHLIGHT_BG_COLOR), DocConstants.DEFAULT_HIGHLIGHT_BG_COLOR); vis.getRenderContext().setVertexFillPaintTransformer( new VertexColor(selectedComponent, defaultBgColor, highlightBgcolor)); Color defaultFgColor = decode(options.get(DocConstants.OPTION_IMG_FOREGROUND_COLOR), DocConstants.DEFAULT_FOREGROUND_COLOR); vis.getRenderContext().setVertexLabelRenderer(new MyVertexLabelRenderer(selectedComponent, defaultFgColor)); vis.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR); BufferedImage image = (BufferedImage) vis.getImage( new Point2D.Double(layout.getSize().getWidth() / 2, layout.getSize().getHeight() / 2), new Dimension(layout.getSize())); ImageIO.write(image, "png", outputFile); } /** * Decodes an hexadecimal color and resolves it as an AWT color. * @param value the value to decode * @param defaultValue the default value to use if value is invalid * @return a color (not null) */ static Color decode(String value, String defaultValue) { Color result; try { result = Color.decode(value); } catch (NumberFormatException e) { Logger logger = Logger.getLogger(GraphUtils.class.getName()); logger.severe("The specified color " + value + " could not be parsed. Back to default value: " + defaultValue); Utils.logException(logger, e); result = Color.decode(defaultValue); } return result; } /** * @author Vincent Zurczak - Linagora */ static class VertexColor implements Transformer<AbstractType, Paint> { private final Component selectedComponent; private final Color defaultBgColor, highlightBgcolor; public VertexColor(Component selectedComponent, Color defaultBgColor, Color highlightBgcolor) { this.selectedComponent = selectedComponent; this.defaultBgColor = defaultBgColor; this.highlightBgcolor = highlightBgcolor; } @Override public Paint transform(AbstractType type) { return type.equals(this.selectedComponent) ? this.highlightBgcolor : this.defaultBgColor; } } /** * @author Vincent Zurczak - Linagora */ static class VertexFont implements Transformer<AbstractType, Font> { @Override public Font transform(AbstractType type) { return GraphUtils.getDefaultFont(); } } /** * @author Vincent Zurczak - Linagora */ static class MyVertexLabelRenderer extends DefaultVertexLabelRenderer { private static final long serialVersionUID = -7669532897039301417L; private final Component selectedComponent; public MyVertexLabelRenderer(Component selectedComponent, Color defaultFgColor) { super(defaultFgColor); this.selectedComponent = selectedComponent; } @Override public <T> java.awt.Component getVertexLabelRendererComponent(JComponent vv, Object value, Font font, boolean isSelected, T vertex) { super.getVertexLabelRendererComponent(vv, value, font, isSelected, vertex); if (!this.selectedComponent.equals(vertex)) super.setForeground(this.pickedVertexLabelColor); return this; } } /** * @author Vincent Zurczak - Linagora */ static class NoStringLabeller implements Transformer<String, String> { @Override public String transform(String v) { return ""; } } /** * @author Vincent Zurczak - Linagora */ static class VertexShape implements Transformer<AbstractType, Shape> { @Override public Shape transform(AbstractType type) { int width = computeShapeWidth(type); return new Ellipse2D.Double(-width / 2.0, -SHAPE_HEIGHT / 2.0, width, SHAPE_HEIGHT); } } }