net.roboconf.doc.generator.internal.GraphUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.roboconf.doc.generator.internal.GraphUtils.java

Source

/**
 * 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);
        }
    }
}