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.transformers; import java.awt.Dimension; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import net.roboconf.core.model.beans.AbstractType; import net.roboconf.core.model.beans.Component; import net.roboconf.doc.generator.internal.GraphUtils; import edu.uci.ics.jung.graph.DirectedOrderedSparseMultigraph; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.visualization.decorators.AbstractEdgeShapeTransformer; import edu.uci.ics.jung.visualization.decorators.EdgeShape; /** * A transformer to find vertex positions for Roboconf's hierarchical relations. * @author Vincent Zurczak - Linagora */ public class HierarchicalTransformer extends AbstractRoboconfTransformer { private static final int MIN_H_MARGIN = 100; private static final int H_PADDING = 50; private static final int V_PADDING = 2 * GraphUtils.SHAPE_HEIGHT; private static final int V_MARGIN = 50; private final Component component; private final Map<AbstractType, Point2D> typeToLocation; private final int maxPerLine; private final List<AbstractType> aloneOnRow; private final Graph<AbstractType, String> graph; private int currentWidth = MIN_H_MARGIN; private int currentHeigth = V_MARGIN; private int maxRowWidth, hMargin; /** * Constructor. * @param component the component whose hierarchy must be displayed * @param ancestors its ancestors * @param children its children * @param maxPerLine the maximum number of vertices per line */ public HierarchicalTransformer(Component component, Collection<AbstractType> ancestors, Collection<AbstractType> children, int maxPerLine) { // Store fields this.component = component; this.maxPerLine = maxPerLine; this.typeToLocation = new HashMap<AbstractType, Point2D>(); this.aloneOnRow = new ArrayList<AbstractType>(); // Compute the effective horizontal margin for this graph this.hMargin = computeHMargin(component); for (AbstractType t : ancestors) this.hMargin = Math.max(this.hMargin, computeHMargin(t)); for (AbstractType t : children) this.hMargin = Math.max(this.hMargin, computeHMargin(t)); // Builds the graph this.graph = new DirectedOrderedSparseMultigraph<AbstractType, String>(); int cpt = 1; for (AbstractType t : ancestors) this.graph.addVertex(t); this.graph.addVertex(component); for (AbstractType t : ancestors) this.graph.addEdge("can contain" + cpt++, t, component); for (AbstractType t : children) { this.graph.addVertex(t); this.graph.addEdge("can contain" + cpt++, component, t); } // In these first steps, vertices are aligned on the left if (!ancestors.isEmpty()) { dealWithOthers(ancestors); this.currentHeigth += V_PADDING; } dealWithMainComponent(); if (!children.isEmpty()) { this.currentHeigth += V_PADDING; dealWithOthers(children); } this.currentHeigth += V_MARGIN; // Center alone vertices for (AbstractType t : this.aloneOnRow) { int width = GraphUtils.computeShapeWidth(t); int newX = (this.maxRowWidth - width) / 2; if (newX > this.hMargin) { Point2D p = transform(t); p.setLocation(newX, p.getY()); } } } /** * Finds the position for the main component. */ private void dealWithMainComponent() { this.typeToLocation.put(this.component, new Point2D.Double(this.hMargin, this.currentHeigth)); int width = H_PADDING + GraphUtils.computeShapeWidth(this.component); this.maxRowWidth = Math.max(this.maxRowWidth, width); this.aloneOnRow.add(this.component); } /** * Finds the position of other components. * @param others a non-null list of components */ private void dealWithOthers(Collection<AbstractType> others) { int col = 1; this.currentWidth = this.hMargin; for (AbstractType t : others) { if (col > this.maxPerLine) { col = 1; this.currentHeigth += V_PADDING + GraphUtils.SHAPE_HEIGHT; this.currentWidth = this.hMargin; } this.typeToLocation.put(t, new Point2D.Double(this.currentWidth, this.currentHeigth)); this.currentWidth += H_PADDING + GraphUtils.computeShapeWidth(t); col++; this.maxRowWidth = Math.max(this.maxRowWidth, this.currentWidth); } if (others.size() == 1) this.aloneOnRow.add(others.iterator().next()); } /* * (non-Javadoc) * @see org.apache.commons.collections15.Transformer * #transform(java.lang.Object) */ @Override public Point2D transform(AbstractType input) { return this.typeToLocation.get(input); } /* * (non-Javadoc) * @see net.roboconf.doc.generator.internal.transformers.AbstractRoboconfTransformer * #getGraphDimension() */ @Override public Dimension getGraphDimension() { return new Dimension(this.maxRowWidth, this.currentHeigth); } /* * (non-Javadoc) * @see net.roboconf.doc.generator.internal.transformers.AbstractRoboconfTransformer * #getGraph() */ @Override public Graph<AbstractType, String> getGraph() { return this.graph; } /* * (non-Javadoc) * @see net.roboconf.doc.generator.internal.transformers.AbstractRoboconfTransformer * #getEdgeShapeTransformer() */ @Override public AbstractEdgeShapeTransformer<AbstractType, String> getEdgeShapeTransformer() { return new EdgeShape.Line<AbstractType, String>(); } /** * Computes an horizontal margin for a given node. * <p> * We used to have a static horizontal margin, but images were * truncated for long names. See roboconf-platform#315 * </p> * * @param input a node (can be null) * @return a positive integer */ private static int computeHMargin(AbstractType input) { int basis = MIN_H_MARGIN; if (input != null && input.getName().length() > 17) { // Beyond 17 characters, we give 3 pixels for every new characters. basis += 3 * (input.getName().length() - 17); } return basis; } }