add org.abego.treelayout
[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9044]
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout;
|
||||
|
||||
/**
|
||||
* Used to configure the tree layout algorithm.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
public interface Configuration<TreeNode> {
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// rootLocation
|
||||
|
||||
/**
|
||||
* Identifies the sides of a rectangle (top, left, ...)
|
||||
*/
|
||||
public enum Location {
|
||||
Top, Left, Bottom, Right
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// alignmentInLevel
|
||||
|
||||
/**
|
||||
* Returns the position of the root node in the diagram.
|
||||
* <p>
|
||||
* By default the root of the tree is located at the top of the diagram.
|
||||
* However one may also put it at the left, right or bottom of the diagram.
|
||||
* <p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>Top (Default)</th>
|
||||
* <th>Left</th>
|
||||
* <th>Right</th>
|
||||
* <th>Bottom</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Top.png"></td>
|
||||
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Left.png"></td>
|
||||
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Right.png"></td>
|
||||
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Bottom.png"></td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @return the position of the root node in the diagram
|
||||
*/
|
||||
Location getRootLocation();
|
||||
|
||||
/**
|
||||
* Possible alignments of a node within a level (centered, towards or away
|
||||
* from root)
|
||||
*/
|
||||
public enum AlignmentInLevel {
|
||||
Center, TowardsRoot, AwayFromRoot
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alignment of "smaller" nodes within a level.
|
||||
* <p>
|
||||
* By default all nodes of one level are centered in the level. However one
|
||||
* may also align them "towards the root" or "away from the root". When the
|
||||
* root is located at the top this means the nodes are aligned "to the top
|
||||
* of the level" or "to the bottom of the level".
|
||||
* <p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>Center (Default)</th>
|
||||
* <th>TowardsRoot ("top of level")</th>
|
||||
* <th>AwayFromRoot ("bottom of level")</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Center.png"></td>
|
||||
* <td style="padding:10px;"><img
|
||||
* src="doc-files/TreeGraphView-TowardsRoot.png"></td>
|
||||
* <td style="padding:10px;"><img
|
||||
* src="doc-files/TreeGraphView-AwayFromRoot.png"></td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* Alignment in level when root is at the left:
|
||||
* </p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>Center (Default)</th>
|
||||
* <th>TowardsRoot ("left of level")</th>
|
||||
* <th>AwayFromRoot<br>
|
||||
* ("right of level")</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td style="padding:10px;"><img
|
||||
* src="doc-files/TreeGraphView-Center-RootLeft.png"></td>
|
||||
* <td style="padding:10px;"><img
|
||||
* src="doc-files/TreeGraphView-TowardsRoot-RootLeft.png"></td>
|
||||
* <td style="padding:10px;"><img
|
||||
* src="doc-files/TreeGraphView-AwayFromRoot-RootLeft.png"></td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <p>
|
||||
* Of cause the alignment also works when the root is at the bottom or at
|
||||
* the right side.
|
||||
* </p>
|
||||
*
|
||||
* @return the alignment of "smaller" nodes within a level
|
||||
*/
|
||||
AlignmentInLevel getAlignmentInLevel();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// gapBetweenLevels/Nodes
|
||||
|
||||
/**
|
||||
* Returns the size of the gap between subsequent levels.
|
||||
* <p>
|
||||
* <img src="doc-files/gapBetweenLevels.png">
|
||||
*
|
||||
* @param nextLevel
|
||||
* [nextLevel > 0]
|
||||
*
|
||||
* @return the size of the gap between level (nextLevel-1) and nextLevel
|
||||
* [result >= 0]
|
||||
*/
|
||||
double getGapBetweenLevels(int nextLevel);
|
||||
|
||||
/**
|
||||
* Returns the size of the minimal gap of nodes within a level.
|
||||
* <p>
|
||||
* In the layout there will be a gap of at least the returned size between
|
||||
* both given nodes.
|
||||
* <p>
|
||||
* <img src="doc-files/gapBetweenNodes.png">
|
||||
* <p>
|
||||
* node1 and node2 are at the same level and are placed next to each other.
|
||||
*
|
||||
* @param node1
|
||||
* @param node2
|
||||
* @return the minimal size of the gap between node1 and node2 [result >= 0]
|
||||
*/
|
||||
double getGapBetweenNodes(TreeNode node1, TreeNode node2);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout;
|
||||
|
||||
/**
|
||||
* Provides the extent (width and height) of a tree node.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
public interface NodeExtentProvider<TreeNode> {
|
||||
/**
|
||||
* Returns the width of the given treeNode.
|
||||
*
|
||||
* @param treeNode
|
||||
* @return [result >= 0]
|
||||
*/
|
||||
double getWidth(TreeNode treeNode);
|
||||
|
||||
/**
|
||||
* Returns the height of the given treeNode.
|
||||
*
|
||||
* @param treeNode
|
||||
* @return [result >= 0]
|
||||
*/
|
||||
double getHeight(TreeNode treeNode);
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout;
|
||||
|
||||
/**
|
||||
* Represents a tree to be used by the {@link TreeLayout}.
|
||||
* <p>
|
||||
* The TreeForTreeLayout interface is designed to best match the implemented
|
||||
* layout algorithm and to ensure the algorithm's time complexity promises in
|
||||
* all possible cases. However in most situation a client must not deal with all
|
||||
* details of this interface and can directly use the
|
||||
* {@link org.abego.treelayout.util.AbstractTreeForTreeLayout} to implement this
|
||||
* interface or even use the
|
||||
* {@link org.abego.treelayout.util.DefaultTreeForTreeLayout} class directly.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
public interface TreeForTreeLayout<TreeNode> {
|
||||
|
||||
/**
|
||||
* Returns the the root of the tree.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @return the root of the tree
|
||||
*/
|
||||
TreeNode getRoot();
|
||||
|
||||
/**
|
||||
* Tells if a node is a leaf in the tree.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param node
|
||||
* @return true iff node is a leaf in the tree, i.e. has no children.
|
||||
*/
|
||||
boolean isLeaf(TreeNode node);
|
||||
|
||||
/**
|
||||
* Tells if a node is a child of a given parentNode.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param node
|
||||
* @param parentNode
|
||||
* @return true iff the node is a child of the given parentNode
|
||||
*/
|
||||
boolean isChildOfParent(TreeNode node, TreeNode parentNode);
|
||||
|
||||
/**
|
||||
* Returns the children of a parent node.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param parentNode
|
||||
* [!isLeaf(parentNode)]
|
||||
* @return the children of the given parentNode, from first to last
|
||||
*/
|
||||
Iterable<TreeNode> getChildren(TreeNode parentNode);
|
||||
|
||||
/**
|
||||
* Returns the children of a parent node, in reverse order.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param parentNode
|
||||
* [!isLeaf(parentNode)]
|
||||
* @return the children of given parentNode, from last to first
|
||||
*/
|
||||
Iterable<TreeNode> getChildrenReverse(TreeNode parentNode);
|
||||
|
||||
/**
|
||||
* Returns the first child of a parent node.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param parentNode
|
||||
* [!isLeaf(parentNode)]
|
||||
* @return the first child of the parentNode
|
||||
*/
|
||||
TreeNode getFirstChild(TreeNode parentNode);
|
||||
|
||||
/**
|
||||
* Returns the last child of a parent node.
|
||||
* <p>
|
||||
*
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param parentNode
|
||||
* [!isLeaf(parentNode)]
|
||||
* @return the last child of the parentNode
|
||||
*/
|
||||
TreeNode getLastChild(TreeNode parentNode);
|
||||
}
|
|
@ -0,0 +1,732 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.abego.treelayout;
|
||||
|
||||
import static org.abego.treelayout.internal.util.Contract.checkArg;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.abego.treelayout.Configuration.AlignmentInLevel;
|
||||
import org.abego.treelayout.Configuration.Location;
|
||||
|
||||
/**
|
||||
* Implements the actual tree layout algorithm.
|
||||
* <p>
|
||||
* The nodes with their final layout can be retrieved through
|
||||
* {@link #getNodeBounds()}.
|
||||
* <p>
|
||||
* See <a href="package-summary.html">this summary</a> to get an overview how to
|
||||
* use TreeLayout.
|
||||
*
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
public class TreeLayout<TreeNode> {
|
||||
/*
|
||||
* Differences between this implementation and original algorithm
|
||||
* --------------------------------------------------------------
|
||||
*
|
||||
* For easier reference the same names (or at least similar names) as in the
|
||||
* paper of Buchheim, Jünger, and Leipert are used in this
|
||||
* implementation. However in the external interface "first" and "last" are
|
||||
* used instead of "left most" and "right most". The implementation also
|
||||
* supports tree layouts with the root at the left (or right) side. In that
|
||||
* case using "left most" would refer to the "top" child, i.e. using "first"
|
||||
* is less confusing.
|
||||
*
|
||||
* Also the y coordinate is not the level but directly refers the y
|
||||
* coordinate of a level, taking node's height and gapBetweenLevels into
|
||||
* account. When the root is at the left or right side the y coordinate
|
||||
* actually becomes an x coordinate.
|
||||
*
|
||||
* Instead of just using a constant "distance" to calculate the position to
|
||||
* the next node we refer to the "size" (width or height) of the node and a
|
||||
* "gapBetweenNodes".
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// tree
|
||||
|
||||
private final TreeForTreeLayout<TreeNode> tree;
|
||||
|
||||
/**
|
||||
* Returns the Tree the layout is created for.
|
||||
*/
|
||||
public TreeForTreeLayout<TreeNode> getTree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// nodeExtentProvider
|
||||
|
||||
private final NodeExtentProvider<TreeNode> nodeExtentProvider;
|
||||
|
||||
/**
|
||||
* Returns the {@link NodeExtentProvider} used by this {@link TreeLayout}.
|
||||
*/
|
||||
public NodeExtentProvider<TreeNode> getNodeExtentProvider() {
|
||||
return nodeExtentProvider;
|
||||
}
|
||||
|
||||
private double getNodeHeight(TreeNode node) {
|
||||
return nodeExtentProvider.getHeight(node);
|
||||
}
|
||||
|
||||
private double getNodeWidth(TreeNode node) {
|
||||
return nodeExtentProvider.getWidth(node);
|
||||
}
|
||||
|
||||
private double getWidthOrHeightOfNode(TreeNode treeNode, boolean returnWidth) {
|
||||
return returnWidth ? getNodeWidth(treeNode) : getNodeHeight(treeNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the level changes in Y-axis (i.e. root location Top or Bottom) the
|
||||
* height of a node is its thickness, otherwise the node's width is its
|
||||
* thickness.
|
||||
* <p>
|
||||
* The thickness of a node is used when calculating the locations of the
|
||||
* levels.
|
||||
*
|
||||
* @param treeNode
|
||||
* @return
|
||||
*/
|
||||
private double getNodeThickness(TreeNode treeNode) {
|
||||
return getWidthOrHeightOfNode(treeNode, !isLevelChangeInYAxis());
|
||||
}
|
||||
|
||||
/**
|
||||
* When the level changes in Y-axis (i.e. root location Top or Bottom) the
|
||||
* width of a node is its size, otherwise the node's height is its size.
|
||||
* <p>
|
||||
* The size of a node is used when calculating the distance between two
|
||||
* nodes.
|
||||
*
|
||||
* @param treeNode
|
||||
* @return
|
||||
*/
|
||||
private double getNodeSize(TreeNode treeNode) {
|
||||
return getWidthOrHeightOfNode(treeNode, isLevelChangeInYAxis());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// configuration
|
||||
|
||||
private final Configuration<TreeNode> configuration;
|
||||
|
||||
/**
|
||||
* Returns the Configuration used by this {@link TreeLayout}.
|
||||
*/
|
||||
public Configuration<TreeNode> getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private boolean isLevelChangeInYAxis() {
|
||||
Location rootLocation = configuration.getRootLocation();
|
||||
return rootLocation == Location.Top || rootLocation == Location.Bottom;
|
||||
}
|
||||
|
||||
private int getLevelChangeSign() {
|
||||
Location rootLocation = configuration.getRootLocation();
|
||||
return rootLocation == Location.Bottom
|
||||
|| rootLocation == Location.Right ? -1 : 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bounds
|
||||
|
||||
private double boundsLeft = Double.MAX_VALUE;
|
||||
private double boundsRight = Double.MIN_VALUE;
|
||||
private double boundsTop = Double.MAX_VALUE;
|
||||
private double boundsBottom = Double.MIN_VALUE;
|
||||
|
||||
private void updateBounds(TreeNode node, double centerX, double centerY) {
|
||||
double width = getNodeWidth(node);
|
||||
double height = getNodeHeight(node);
|
||||
double left = centerX - width / 2;
|
||||
double right = centerX + width / 2;
|
||||
double top = centerY - height / 2;
|
||||
double bottom = centerY + height / 2;
|
||||
if (boundsLeft > left) {
|
||||
boundsLeft = left;
|
||||
}
|
||||
if (boundsRight < right) {
|
||||
boundsRight = right;
|
||||
}
|
||||
if (boundsTop > top) {
|
||||
boundsTop = top;
|
||||
}
|
||||
if (boundsBottom < bottom) {
|
||||
boundsBottom = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of the tree layout.
|
||||
* <p>
|
||||
* The bounds of a TreeLayout is the smallest rectangle containing the
|
||||
* bounds of all nodes in the layout. It always starts at (0,0).
|
||||
*
|
||||
* @return the bounds of the tree layout
|
||||
*/
|
||||
public Rectangle2D getBounds() {
|
||||
return new Rectangle2D.Double(0, 0, boundsRight - boundsLeft,
|
||||
boundsBottom - boundsTop);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// size of level
|
||||
|
||||
private final List<Double> sizeOfLevel = new ArrayList<Double>();
|
||||
|
||||
private void calcSizeOfLevels(TreeNode node, int level) {
|
||||
double oldSize;
|
||||
if (sizeOfLevel.size() <= level) {
|
||||
sizeOfLevel.add(Double.valueOf(0));
|
||||
oldSize = 0;
|
||||
} else {
|
||||
oldSize = sizeOfLevel.get(level);
|
||||
}
|
||||
|
||||
double size = getNodeThickness(node);
|
||||
// size = nodeExtentProvider.getHeight(node);
|
||||
if (oldSize < size) {
|
||||
sizeOfLevel.set(level, size);
|
||||
}
|
||||
|
||||
if (!tree.isLeaf(node)) {
|
||||
for (TreeNode child : tree.getChildren(node)) {
|
||||
calcSizeOfLevels(child, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of levels of the tree.
|
||||
*
|
||||
* @return [level > 0]
|
||||
*/
|
||||
public int getLevelCount() {
|
||||
return sizeOfLevel.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of a level.
|
||||
* <p>
|
||||
* When the root is located at the top or bottom the size of a level is the
|
||||
* maximal height of the nodes of that level. When the root is located at
|
||||
* the left or right the size of a level is the maximal width of the nodes
|
||||
* of that level.
|
||||
*
|
||||
* @param level
|
||||
* @return the size of the level [level >= 0 && level < levelCount]
|
||||
*/
|
||||
public double getSizeOfLevel(int level) {
|
||||
checkArg(level >= 0, "level must be >= 0");
|
||||
checkArg(level < getLevelCount(), "level must be < levelCount");
|
||||
|
||||
return sizeOfLevel.get(level);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// NormalizedPosition
|
||||
|
||||
/**
|
||||
* The algorithm calculates the position starting with the root at 0. I.e.
|
||||
* the left children will get negative positions. However we want the result
|
||||
* to be normalized to (0,0).
|
||||
* <p>
|
||||
* {@link NormalizedPosition} will normalize the position (given relative to
|
||||
* the root position), taking the current bounds into account. This way the
|
||||
* left most node bounds will start at x = 0, the top most node bounds at y
|
||||
* = 0.
|
||||
*/
|
||||
private class NormalizedPosition extends Point2D {
|
||||
private double x_relativeToRoot;
|
||||
private double y_relativeToRoot;
|
||||
|
||||
public NormalizedPosition(double x_relativeToRoot,
|
||||
double y_relativeToRoot) {
|
||||
setLocation(x_relativeToRoot, y_relativeToRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return x_relativeToRoot - boundsLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return y_relativeToRoot - boundsTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
// never called from outside
|
||||
public void setLocation(double x_relativeToRoot, double y_relativeToRoot) {
|
||||
this.x_relativeToRoot = x_relativeToRoot;
|
||||
this.y_relativeToRoot = y_relativeToRoot;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// The Algorithm
|
||||
|
||||
private final Map<TreeNode, Double> mod = new HashMap<TreeNode, Double>();
|
||||
private final Map<TreeNode, TreeNode> thread = new HashMap<TreeNode, TreeNode>();
|
||||
private final Map<TreeNode, Double> prelim = new HashMap<TreeNode, Double>();
|
||||
private final Map<TreeNode, Double> change = new HashMap<TreeNode, Double>();
|
||||
private final Map<TreeNode, Double> shift = new HashMap<TreeNode, Double>();
|
||||
private final Map<TreeNode, TreeNode> ancestor = new HashMap<TreeNode, TreeNode>();
|
||||
private final Map<TreeNode, Integer> number = new HashMap<TreeNode, Integer>();
|
||||
private final Map<TreeNode, Point2D> positions = new HashMap<TreeNode, Point2D>();
|
||||
|
||||
private double getMod(TreeNode node) {
|
||||
Double d = mod.get(node);
|
||||
return d != null ? d.doubleValue() : 0;
|
||||
}
|
||||
|
||||
private void setMod(TreeNode node, double d) {
|
||||
mod.put(node, d);
|
||||
}
|
||||
|
||||
private TreeNode getThread(TreeNode node) {
|
||||
TreeNode n = thread.get(node);
|
||||
return n != null ? n : null;
|
||||
}
|
||||
|
||||
private void setThread(TreeNode node, TreeNode thread) {
|
||||
this.thread.put(node, thread);
|
||||
}
|
||||
|
||||
private TreeNode getAncestor(TreeNode node) {
|
||||
TreeNode n = ancestor.get(node);
|
||||
return n != null ? n : node;
|
||||
}
|
||||
|
||||
private void setAncestor(TreeNode node, TreeNode ancestor) {
|
||||
this.ancestor.put(node, ancestor);
|
||||
}
|
||||
|
||||
private double getPrelim(TreeNode node) {
|
||||
Double d = prelim.get(node);
|
||||
return d != null ? d.doubleValue() : 0;
|
||||
}
|
||||
|
||||
private void setPrelim(TreeNode node, double d) {
|
||||
prelim.put(node, d);
|
||||
}
|
||||
|
||||
private double getChange(TreeNode node) {
|
||||
Double d = change.get(node);
|
||||
return d != null ? d.doubleValue() : 0;
|
||||
}
|
||||
|
||||
private void setChange(TreeNode node, double d) {
|
||||
change.put(node, d);
|
||||
}
|
||||
|
||||
private double getShift(TreeNode node) {
|
||||
Double d = shift.get(node);
|
||||
return d != null ? d.doubleValue() : 0;
|
||||
}
|
||||
|
||||
private void setShift(TreeNode node, double d) {
|
||||
shift.put(node, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* The distance of two nodes is the distance of the centers of both noded.
|
||||
* <p>
|
||||
* I.e. the distance includes the gap between the nodes and half of the
|
||||
* sizes of the nodes.
|
||||
*
|
||||
* @param v
|
||||
* @param w
|
||||
* @return the distance between node v and w
|
||||
*/
|
||||
private double getDistance(TreeNode v, TreeNode w) {
|
||||
double sizeOfNodes = getNodeSize(v) + getNodeSize(w);
|
||||
|
||||
double distance = sizeOfNodes / 2
|
||||
+ configuration.getGapBetweenNodes(v, w);
|
||||
return distance;
|
||||
}
|
||||
|
||||
private TreeNode nextLeft(TreeNode v) {
|
||||
return tree.isLeaf(v) ? getThread(v) : tree.getFirstChild(v);
|
||||
}
|
||||
|
||||
private TreeNode nextRight(TreeNode v) {
|
||||
return tree.isLeaf(v) ? getThread(v) : tree.getLastChild(v);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* [tree.isChildOfParent(node, parentNode)]
|
||||
* @param parentNode
|
||||
* parent of node
|
||||
* @return
|
||||
*/
|
||||
private int getNumber(TreeNode node, TreeNode parentNode) {
|
||||
Integer n = number.get(node);
|
||||
if (n == null) {
|
||||
int i = 1;
|
||||
for (TreeNode child : tree.getChildren(parentNode)) {
|
||||
number.put(child, i++);
|
||||
}
|
||||
n = number.get(node);
|
||||
}
|
||||
|
||||
return n.intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vIMinus
|
||||
* @param v
|
||||
* @param parentOfV
|
||||
* @param defaultAncestor
|
||||
* @return the greatest distinct ancestor of vIMinus and its right neighbor
|
||||
* v
|
||||
*/
|
||||
private TreeNode ancestor(TreeNode vIMinus, TreeNode v, TreeNode parentOfV,
|
||||
TreeNode defaultAncestor) {
|
||||
TreeNode ancestor = getAncestor(vIMinus);
|
||||
|
||||
// when the ancestor of vIMinus is a sibling of v (i.e. has the same
|
||||
// parent as v) it is also the greatest distinct ancestor vIMinus and
|
||||
// v. Otherwise it is the defaultAncestor
|
||||
|
||||
return tree.isChildOfParent(ancestor, parentOfV) ? ancestor
|
||||
: defaultAncestor;
|
||||
}
|
||||
|
||||
private void moveSubtree(TreeNode wMinus, TreeNode wPlus, TreeNode parent,
|
||||
double shift) {
|
||||
|
||||
int subtrees = getNumber(wPlus, parent) - getNumber(wMinus, parent);
|
||||
setChange(wPlus, getChange(wPlus) - shift / subtrees);
|
||||
setShift(wPlus, getShift(wPlus) + shift);
|
||||
setChange(wMinus, getChange(wMinus) + shift / subtrees);
|
||||
setPrelim(wPlus, getPrelim(wPlus) + shift);
|
||||
setMod(wPlus, getMod(wPlus) + shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* In difference to the original algorithm we also pass in the leftSibling
|
||||
* and the parent of v.
|
||||
* <p>
|
||||
* <b>Why adding the parameter 'parent of v' (parentOfV) ?</b>
|
||||
* <p>
|
||||
* In this method we need access to the parent of v. Not every tree
|
||||
* implementation may support efficient (i.e. constant time) access to it.
|
||||
* On the other hand the (only) caller of this method can provide this
|
||||
* information with only constant extra time.
|
||||
* <p>
|
||||
* Also we need access to the "left most sibling" of v. Not every tree
|
||||
* implementation may support efficient (i.e. constant time) access to it.
|
||||
* On the other hand the "left most sibling" of v is also the "first child"
|
||||
* of the parent of v. The first child of a parent node we can get in
|
||||
* constant time. As we got the parent of v we can so also get the
|
||||
* "left most sibling" of v in constant time.
|
||||
* <p>
|
||||
* <b>Why adding the parameter 'leftSibling' ?</b>
|
||||
* <p>
|
||||
* In this method we need access to the "left sibling" of v. Not every tree
|
||||
* implementation may support efficient (i.e. constant time) access to it.
|
||||
* However it is easy for the caller of this method to provide this
|
||||
* information with only constant extra time.
|
||||
* <p>
|
||||
* <p>
|
||||
* <p>
|
||||
* In addition these extra parameters avoid the need for
|
||||
* {@link TreeForTreeLayout} to include extra methods "getParent",
|
||||
* "getLeftSibling", or "getLeftMostSibling". This keeps the interface
|
||||
* {@link TreeForTreeLayout} small and avoids redundant implementations.
|
||||
*
|
||||
* @param v
|
||||
* @param defaultAncestor
|
||||
* @param leftSibling
|
||||
* [nullable] the left sibling v, if there is any
|
||||
* @param parentOfV
|
||||
* the parent of v
|
||||
* @return the (possibly changes) defaultAncestor
|
||||
*/
|
||||
private TreeNode apportion(TreeNode v, TreeNode defaultAncestor,
|
||||
TreeNode leftSibling, TreeNode parentOfV) {
|
||||
TreeNode w = leftSibling;
|
||||
if (w == null) {
|
||||
// v has no left sibling
|
||||
return defaultAncestor;
|
||||
}
|
||||
// v has left sibling w
|
||||
|
||||
// The following variables "v..." are used to traverse the contours to
|
||||
// the subtrees. "Minus" refers to the left, "Plus" to the right
|
||||
// subtree. "I" refers to the "inside" and "O" to the outside contour.
|
||||
TreeNode vOPlus = v;
|
||||
TreeNode vIPlus = v;
|
||||
TreeNode vIMinus = w;
|
||||
// get leftmost sibling of vIPlus, i.e. get the leftmost sibling of
|
||||
// v, i.e. the leftmost child of the parent of v (which is passed
|
||||
// in)
|
||||
TreeNode vOMinus = tree.getFirstChild(parentOfV);
|
||||
|
||||
Double sIPlus = getMod(vIPlus);
|
||||
Double sOPlus = getMod(vOPlus);
|
||||
Double sIMinus = getMod(vIMinus);
|
||||
Double sOMinus = getMod(vOMinus);
|
||||
|
||||
TreeNode nextRightVIMinus = nextRight(vIMinus);
|
||||
TreeNode nextLeftVIPlus = nextLeft(vIPlus);
|
||||
|
||||
while (nextRightVIMinus != null && nextLeftVIPlus != null) {
|
||||
vIMinus = nextRightVIMinus;
|
||||
vIPlus = nextLeftVIPlus;
|
||||
vOMinus = nextLeft(vOMinus);
|
||||
vOPlus = nextRight(vOPlus);
|
||||
setAncestor(vOPlus, v);
|
||||
double shift = (getPrelim(vIMinus) + sIMinus)
|
||||
- (getPrelim(vIPlus) + sIPlus)
|
||||
+ getDistance(vIMinus, vIPlus);
|
||||
|
||||
if (shift > 0) {
|
||||
moveSubtree(ancestor(vIMinus, v, parentOfV, defaultAncestor),
|
||||
v, parentOfV, shift);
|
||||
sIPlus = sIPlus + shift;
|
||||
sOPlus = sOPlus + shift;
|
||||
}
|
||||
sIMinus = sIMinus + getMod(vIMinus);
|
||||
sIPlus = sIPlus + getMod(vIPlus);
|
||||
sOMinus = sOMinus + getMod(vOMinus);
|
||||
sOPlus = sOPlus + getMod(vOPlus);
|
||||
|
||||
nextRightVIMinus = nextRight(vIMinus);
|
||||
nextLeftVIPlus = nextLeft(vIPlus);
|
||||
}
|
||||
|
||||
if (nextRightVIMinus != null && nextRight(vOPlus) == null) {
|
||||
setThread(vOPlus, nextRightVIMinus);
|
||||
setMod(vOPlus, getMod(vOPlus) + sIMinus - sOPlus);
|
||||
}
|
||||
|
||||
if (nextLeftVIPlus != null && nextLeft(vOMinus) == null) {
|
||||
setThread(vOMinus, nextLeftVIPlus);
|
||||
setMod(vOMinus, getMod(vOMinus) + sIPlus - sOMinus);
|
||||
defaultAncestor = v;
|
||||
}
|
||||
return defaultAncestor;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param v
|
||||
* [!tree.isLeaf(v)]
|
||||
*/
|
||||
private void executeShifts(TreeNode v) {
|
||||
double shift = 0;
|
||||
double change = 0;
|
||||
for (TreeNode w : tree.getChildrenReverse(v)) {
|
||||
change = change + getChange(w);
|
||||
setPrelim(w, getPrelim(w) + shift);
|
||||
setMod(w, getMod(w) + shift);
|
||||
shift = shift + getShift(w) + change;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In difference to the original algorithm we also pass in the leftSibling
|
||||
* (see {@link #apportion(Object, Object, Object, Object)} for a
|
||||
* motivation).
|
||||
*
|
||||
* @param v
|
||||
* @param leftSibling
|
||||
* [nullable] the left sibling v, if there is any
|
||||
*/
|
||||
private void firstWalk(TreeNode v, TreeNode leftSibling) {
|
||||
if (tree.isLeaf(v)) {
|
||||
// No need to set prelim(v) to 0 as the getter takes care of this.
|
||||
|
||||
TreeNode w = leftSibling;
|
||||
if (w != null) {
|
||||
// v has left sibling
|
||||
|
||||
setPrelim(v, getPrelim(w) + getDistance(v, w));
|
||||
}
|
||||
|
||||
} else {
|
||||
// v is not a leaf
|
||||
|
||||
TreeNode defaultAncestor = tree.getFirstChild(v);
|
||||
TreeNode previousChild = null;
|
||||
for (TreeNode w : tree.getChildren(v)) {
|
||||
firstWalk(w, previousChild);
|
||||
defaultAncestor = apportion(w, defaultAncestor, previousChild,
|
||||
v);
|
||||
previousChild = w;
|
||||
}
|
||||
executeShifts(v);
|
||||
double midpoint = (getPrelim(tree.getFirstChild(v)) + getPrelim(tree
|
||||
.getLastChild(v))) / 2.0;
|
||||
TreeNode w = leftSibling;
|
||||
if (w != null) {
|
||||
// v has left sibling
|
||||
|
||||
setPrelim(v, getPrelim(w) + getDistance(v, w));
|
||||
setMod(v, getPrelim(v) - midpoint);
|
||||
|
||||
} else {
|
||||
// v has no left sibling
|
||||
|
||||
setPrelim(v, midpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In difference to the original algorithm we also pass in extra level
|
||||
* information.
|
||||
*
|
||||
* @param v
|
||||
* @param m
|
||||
* @param level
|
||||
* @param levelStart
|
||||
*/
|
||||
private void secondWalk(TreeNode v, double m, int level, double levelStart) {
|
||||
// construct the position from the prelim and the level information
|
||||
|
||||
// The rootLocation affects the way how x and y are changed and in what
|
||||
// direction.
|
||||
double levelChangeSign = getLevelChangeSign();
|
||||
boolean levelChangeOnYAxis = isLevelChangeInYAxis();
|
||||
double levelSize = getSizeOfLevel(level);
|
||||
|
||||
double x = getPrelim(v) + m;
|
||||
|
||||
double y;
|
||||
AlignmentInLevel alignment = configuration.getAlignmentInLevel();
|
||||
if (alignment == AlignmentInLevel.Center) {
|
||||
y = levelStart + levelChangeSign * (levelSize / 2);
|
||||
} else if (alignment == AlignmentInLevel.TowardsRoot) {
|
||||
y = levelStart + levelChangeSign * (getNodeThickness(v) / 2);
|
||||
} else {
|
||||
y = levelStart + levelSize - levelChangeSign
|
||||
* (getNodeThickness(v) / 2);
|
||||
}
|
||||
|
||||
if (!levelChangeOnYAxis) {
|
||||
double t = x;
|
||||
x = y;
|
||||
y = t;
|
||||
}
|
||||
|
||||
positions.put(v, new NormalizedPosition(x, y));
|
||||
|
||||
// update the bounds
|
||||
updateBounds(v, x, y);
|
||||
|
||||
// recurse
|
||||
if (!tree.isLeaf(v)) {
|
||||
double nextLevelStart = levelStart
|
||||
+ (levelSize + configuration.getGapBetweenLevels(level + 1))
|
||||
* levelChangeSign;
|
||||
for (TreeNode w : tree.getChildren(v)) {
|
||||
secondWalk(w, m + getMod(v), level + 1, nextLevelStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// nodeBounds
|
||||
|
||||
private Map<TreeNode, Rectangle2D.Double> nodeBounds;
|
||||
|
||||
/**
|
||||
* Returns the layout of the tree nodes by mapping each node of the tree to
|
||||
* its bounds (position and size).
|
||||
* <p>
|
||||
* For each rectangle x and y will be >= 0. At least one rectangle will have
|
||||
* an x == 0 and at least one rectangle will have an y == 0.
|
||||
*
|
||||
* @return maps each node of the tree to its bounds (position and size).
|
||||
*/
|
||||
public Map<TreeNode, Rectangle2D.Double> getNodeBounds() {
|
||||
if (nodeBounds == null) {
|
||||
nodeBounds = new HashMap<TreeNode, Rectangle2D.Double>();
|
||||
for (Entry<TreeNode, Point2D> entry : positions.entrySet()) {
|
||||
TreeNode node = entry.getKey();
|
||||
Point2D pos = entry.getValue();
|
||||
double w = getNodeWidth(node);
|
||||
double h = getNodeHeight(node);
|
||||
double x = pos.getX() - w / 2;
|
||||
double y = pos.getY() - h / 2;
|
||||
nodeBounds.put(node, new Rectangle2D.Double(x, y, w, h));
|
||||
}
|
||||
}
|
||||
return nodeBounds;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// constructor
|
||||
|
||||
/**
|
||||
* Creates a TreeLayout for a given tree.
|
||||
* <p>
|
||||
* In addition to the tree the {@link NodeExtentProvider} and the
|
||||
* {@link Configuration} must be given.
|
||||
*/
|
||||
public TreeLayout(TreeForTreeLayout<TreeNode> tree,
|
||||
NodeExtentProvider<TreeNode> nodeExtentProvider,
|
||||
Configuration<TreeNode> configuration) {
|
||||
this.tree = tree;
|
||||
this.nodeExtentProvider = nodeExtentProvider;
|
||||
this.configuration = configuration;
|
||||
|
||||
// No need to explicitly set mod, thread and ancestor as their getters
|
||||
// are taking care of the initial values. This avoids a full tree walk
|
||||
// through and saves some memory as no entries are added for
|
||||
// "initial values".
|
||||
|
||||
TreeNode r = tree.getRoot();
|
||||
firstWalk(r, null);
|
||||
calcSizeOfLevels(r, 0);
|
||||
secondWalk(r, -getPrelim(r), 0, 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
[The "BSD license"]
|
||||
Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of the abego Software GmbH nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" standalone="no" ?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg width="260" height="176" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<line x1="177" y1="10" x2="110" y2="80" style="stroke:black; stroke-width:2px;" />
|
||||
<line x1="110" y1="80" x2="40" y2="158" style="stroke:black; stroke-width:2px;" />
|
||||
<line x1="110" y1="80" x2="110" y2="158" style="stroke:black; stroke-width:2px;" />
|
||||
<line x1="110" y1="80" x2="180" y2="158" style="stroke:black; stroke-width:2px;" />
|
||||
<line x1="177" y1="10" x2="245" y2="80" style="stroke:black; stroke-width:2px;" />
|
||||
<line x1="245" y1="80" x2="245" y2="158" style="stroke:black; stroke-width:2px;" />
|
||||
<rect x="91" y="149" width="38" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="98" y="161" style="font-family:sans-serif;font-size:12px;">
|
||||
n1.2
|
||||
</text>
|
||||
<rect x="1" y="141" width="78" height="34" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="8" y="153" style="font-family:sans-serif;font-size:12px;">
|
||||
n1.1
|
||||
</text>
|
||||
<text x="8" y="165" style="font-family:sans-serif;font-size:12px;">
|
||||
(first node)
|
||||
</text>
|
||||
<rect x="231" y="71" width="28" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="238" y="83" style="font-family:sans-serif;font-size:12px;">
|
||||
n2
|
||||
</text>
|
||||
<rect x="158" y="1" width="38" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="165" y="13" style="font-family:sans-serif;font-size:12px;">
|
||||
root
|
||||
</text>
|
||||
<rect x="231" y="149" width="28" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="238" y="161" style="font-family:sans-serif;font-size:12px;">
|
||||
n2
|
||||
</text>
|
||||
<rect x="96" y="71" width="28" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="103" y="83" style="font-family:sans-serif;font-size:12px;">
|
||||
n1
|
||||
</text>
|
||||
<rect x="141" y="141" width="78" height="34" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
|
||||
<text x="148" y="153" style="font-family:sans-serif;font-size:12px;">
|
||||
n1.3
|
||||
</text>
|
||||
<text x="148" y="165" style="font-family:sans-serif;font-size:12px;">
|
||||
(last node)
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.internal.util;
|
||||
|
||||
|
||||
/**
|
||||
* A set of methods to support some sort of "Design by Contract" programming.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class Contract {
|
||||
|
||||
public static void checkArg(boolean isOK, String s) {
|
||||
if (!isOK) {
|
||||
throw new IllegalArgumentException(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkState(boolean isOK, String s) {
|
||||
if (!isOK) {
|
||||
throw new IllegalStateException(s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.internal.util.java.lang;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.abego.treelayout.internal.util.java.util.IteratorUtil;
|
||||
|
||||
/**
|
||||
* Util (general purpose) methods dealing with {@link Iterable}.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class IterableUtil {
|
||||
private static class ReverseIterable<T> implements Iterable<T> {
|
||||
private List<T> list;
|
||||
|
||||
public ReverseIterable(List<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return IteratorUtil.createReverseIterator(list);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterable} with an iterator iterating the given list
|
||||
* from the end to the start.
|
||||
* <p>
|
||||
* I.e. the iterator does the reverse of the {@link List#iterator()}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param list
|
||||
* @return a reverse {@link Iterable} of the list
|
||||
*/
|
||||
public static <T> Iterable<T> createReverseIterable(List<T> list) {
|
||||
// When the list is empty we can use the "forward" iterable (i.e. the
|
||||
// list itself) also as the reverseIterable as it will do nothing.
|
||||
if (list.size() == 0) {
|
||||
return list;
|
||||
}
|
||||
|
||||
return new ReverseIterable<T>(list);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internally used classes - subject to change without notice; client code must
|
||||
* not use these classes.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*/
|
||||
package org.abego.treelayout.internal.util.java.lang;
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.internal.util.java.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* Util (general purpose) methods dealing with {@link Iterator}.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class IteratorUtil {
|
||||
|
||||
private static class ReverseIterator<T> implements Iterator<T> {
|
||||
private ListIterator<T> listIterator;
|
||||
|
||||
public ReverseIterator(List<T> list) {
|
||||
this.listIterator = list.listIterator(list.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return listIterator.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return listIterator.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
listIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterator} iterating the given list from the end to the
|
||||
* start.
|
||||
* <p>
|
||||
* I.e. the iterator does the reverse of the {@link List#iterator()}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param list
|
||||
* @return a reverse {@link Iterator} of the list
|
||||
*/
|
||||
public static <T> Iterator<T> createReverseIterator(List<T> list) {
|
||||
return new ReverseIterator<T>(list);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.internal.util.java.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Util (general purpose) methods dealing with {@link List}.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ListUtil {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T>
|
||||
* @param list [!list.isEmpty()]
|
||||
* @return the last element of the list
|
||||
*/
|
||||
public static <T> T getLast(List<T> list) {
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/**
|
||||
* Internally used classes - subject to change without notice; client code must
|
||||
* not use these classes.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.abego.treelayout.internal.util.java.util;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/**
|
||||
* Internally used classes - subject to change without notice; client code must
|
||||
* not use these classes.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.abego.treelayout.internal.util;
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
Efficiently create compact, highly customizable tree layouts.
|
||||
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
The TreeLayout creates tree layouts for arbitrary trees. It is not restricted
|
||||
to a specific output or format, but can be used for any kind of two dimensional
|
||||
diagram. Examples are Swing based components, SVG files, and many more. This is
|
||||
possible because TreeLayout separates the layout of a tree from the actual
|
||||
rendering.<p>
|
||||
|
||||
To use the TreeLayout you mainly need to supply an instance of the
|
||||
{@link org.abego.treelayout.TreeLayout TreeLayout} class with the nodes of the tree (including "children"
|
||||
links), together with the "size" of each node. In addition you can configure
|
||||
the layout by specifying parameters like "gap between levels" etc..<p>
|
||||
|
||||
Based on this information TreeLayout creates a compact, nice looking layout.
|
||||
The layout has the following properties [2]:
|
||||
|
||||
<ol>
|
||||
<li>The layout displays the hierarchical structure of the tree, i.e. the
|
||||
y-coordinate of a node is given by its level. (*)</li>
|
||||
<li>The edges do not cross each other and nodes on the same level have a
|
||||
minimal horizontal distance.</li>
|
||||
<li>The drawing of a subtree does not depend on its position in the tree, i.e.
|
||||
isomorphic subtrees are drawn identically up to translation.</li>
|
||||
<li>The order of the children of a node is displayed in the drawing.</li>
|
||||
<li>The algorithm works symmetrically, i.e. the drawing of the reflection of a
|
||||
tree is the reflected drawing of the original tree.</li>
|
||||
</ol>
|
||||
<i> (*) When the root is at the left or right (see "Root Location") the
|
||||
x-coordinate of a node is given by its level</i><p>
|
||||
|
||||
Here an example tree layout:<p>
|
||||
<img src="doc-files/TreeGraphView-Top.png">
|
||||
|
||||
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
To use the TreeLayout you will create a {@link org.abego.treelayout.TreeLayout TreeLayout} instance with:
|
||||
<ul>
|
||||
<li>a tree, accessible through the {@link org.abego.treelayout.TreeForTreeLayout TreeForTreeLayout} interface, </li>
|
||||
<li>a {@link org.abego.treelayout.NodeExtentProvider NodeExtentProvider}, and</li>
|
||||
<li>a {@link org.abego.treelayout.Configuration Configuration}.</li>
|
||||
</ul>
|
||||
Using these objects the TreeLayout will then calculate the layout and provide
|
||||
the result through the method {@link org.abego.treelayout.TreeLayout#getNodeBounds() getNodeBounds}.
|
||||
|
||||
|
||||
<h3>TreeForTreeLayout</h3>
|
||||
|
||||
The TreeLayout works on any kind of tree and uses the {@link org.abego.treelayout.TreeForTreeLayout TreeForTreeLayout}
|
||||
interface to access such a tree.<p>
|
||||
|
||||
To use the TreeLayout you therefore need to provide it with a TreeForTreeLayout
|
||||
implementation. In most situations you will not need to deal with all details
|
||||
of that interface, but create an implementation by extending
|
||||
{@link org.abego.treelayout.util.AbstractTreeForTreeLayout AbstractTreeForTreeLayout}, or even use the class
|
||||
{@link org.abego.treelayout.util.DefaultTreeForTreeLayout DefaultTreeForTreeLayout} directly.
|
||||
|
||||
|
||||
<h4>Example: Extending AbstractTreeForTreeLayout</h4>
|
||||
|
||||
Assume you have a tree consisting of nodes of type StringTreeNode:<p>
|
||||
<img src="doc-files/StringTreeNodeUML.png"><p>
|
||||
|
||||
As StringTreeNode provides the children in a list and you can get the parent for
|
||||
each node you can extend {@link org.abego.treelayout.util.AbstractTreeForTreeLayout AbstractTreeForTreeLayout} to create your
|
||||
TreeForTreeLayout implementation. You only need to implement two methods and
|
||||
the constructor:<p>
|
||||
<pre>
|
||||
public class StringTreeAsTreeForTreeLayout extends
|
||||
AbstractTreeForTreeLayout<StringTreeNode> {
|
||||
|
||||
public StringTreeAsTreeForTreeLayout(StringTreeNode root) {
|
||||
super(root);
|
||||
}
|
||||
|
||||
public StringTreeNode getParent(StringTreeNode node) {
|
||||
return node.getParent();
|
||||
}
|
||||
|
||||
public List<StringTreeNode> getChildrenList(StringTreeNode parentNode) {
|
||||
return parentNode.getChildren();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<i>(Make sure to check out the performance constraints of {@link org.abego.treelayout.util.AbstractTreeForTreeLayout#getChildrenList(Object) getChildrenList} and {@link org.abego.treelayout.util.AbstractTreeForTreeLayout#getParent(Object) getParent}.)</i>
|
||||
|
||||
|
||||
|
||||
<h4>Example: Using the DefaultTreeForTreeLayout</h4>
|
||||
|
||||
Assume you want to create a tree with TextInBox items as nodes:<p>
|
||||
<img src="doc-files/TextInBoxUML.png"><p>
|
||||
|
||||
As you have no own tree implementation yet you may as well use
|
||||
{@link org.abego.treelayout.util.DefaultTreeForTreeLayout DefaultTreeForTreeLayout} to create the tree:<p>
|
||||
<pre>
|
||||
TextInBox root = new TextInBox("root", 40, 20);
|
||||
TextInBox n1 = new TextInBox("n1", 30, 20);
|
||||
TextInBox n1_1 = new TextInBox("n1.1\n(first node)", 80, 36);
|
||||
TextInBox n1_2 = new TextInBox("n1.2", 40, 20);
|
||||
TextInBox n1_3 = new TextInBox("n1.3\n(last node)", 80, 36);
|
||||
TextInBox n2 = new TextInBox("n2", 30, 20);
|
||||
TextInBox n2_1 = new TextInBox("n2", 30, 20);
|
||||
|
||||
DefaultTreeForTreeLayout<TextInBox> tree =
|
||||
new DefaultTreeForTreeLayout<TextInBox>(root);
|
||||
tree.addChild(root, n1);
|
||||
tree.addChild(n1, n1_1);
|
||||
tree.addChild(n1, n1_2);
|
||||
tree.addChild(n1, n1_3);
|
||||
tree.addChild(root, n2);
|
||||
tree.addChild(n2, n2_1);
|
||||
</pre><p>
|
||||
This will create a tree like this:<p>
|
||||
<img src="doc-files/svgdemo.png">
|
||||
|
||||
|
||||
<h3>NodeExtentProvider</h3>
|
||||
|
||||
TreeLayout also needs to know the extent (width and height) of each node in the
|
||||
tree. This information is provided through the {@link org.abego.treelayout.NodeExtentProvider NodeExtentProvider}.<p>
|
||||
|
||||
If all nodes have the same size you can use a {@link org.abego.treelayout.util.FixedNodeExtentProvider FixedNodeExtentProvider}
|
||||
instance with the proper width and height.<p>
|
||||
|
||||
In general you will create your own NodeExtentProvider implementation.
|
||||
<h4>Example</h4>
|
||||
|
||||
Assume you want to create a tree with TextInBox items as nodes:<p>
|
||||
<img src="doc-files/TextInBoxUML.png"><p>
|
||||
|
||||
Here each node contains its width and height. So your NodeExtentProvider may
|
||||
look like this:<p><pre>
|
||||
public class TextInBoxNodeExtentProvider implements
|
||||
NodeExtentProvider<TextInBox> {
|
||||
|
||||
@Override
|
||||
public double getWidth(TextInBox treeNode) {
|
||||
return treeNode.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHeight(TextInBox treeNode) {
|
||||
return treeNode.height;
|
||||
}
|
||||
}</pre>
|
||||
|
||||
|
||||
|
||||
<h3>Configuration</h3>
|
||||
|
||||
You can use a {@link org.abego.treelayout.Configuration Configuration} to customize various aspects of the
|
||||
TreeLayout:
|
||||
<ul>
|
||||
<li>the gap between levels,</li>
|
||||
<li>the minimal gap between nodes,</li>
|
||||
<li>the position of the root node,</li>
|
||||
<li>the alignment of smaller nodes within a level.</li>
|
||||
</ul>
|
||||
Most of the times using the {@link org.abego.treelayout.util.DefaultConfiguration DefaultConfiguration} class will be sufficient.
|
||||
|
||||
<h4>Root Position</h4>
|
||||
|
||||
By default the root of the tree is located at the top of the diagram. However
|
||||
one may also put it at the left, right or bottom of the diagram.
|
||||
<p>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>Top (Default)</th>
|
||||
<th>Left</th>
|
||||
<th>Right</th>
|
||||
<th>Bottom</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Top.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Left.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Right.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Bottom.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
See {@link org.abego.treelayout.Configuration#getRootLocation() getRootLocation}.
|
||||
|
||||
|
||||
|
||||
|
||||
<h4>Alignment in Level</h4>
|
||||
|
||||
By default all nodes of one level are centered in the level. However
|
||||
one may also align them "towards the root" or "away from the root". When the
|
||||
root is located at the top this means the nodes are aligned "to the top of the
|
||||
level" or "to the bottom of the level".
|
||||
<p>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>Center (Default)</th>
|
||||
<th>TowardsRoot ("top of level")</th>
|
||||
<th>AwayFromRoot ("bottom of level")</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Center.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-TowardsRoot.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-AwayFromRoot.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>Alignment in level when root is at the left:</p>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>Center (Default)</th>
|
||||
<th>TowardsRoot ("left of level")</th>
|
||||
<th>AwayFromRoot<br>("right of level")</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Center-RootLeft.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-TowardsRoot-RootLeft.png"></td>
|
||||
<td style="padding:10px;"><img src="doc-files/TreeGraphView-AwayFromRoot-RootLeft.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>Of cause the alignment also works when the root is at the bottom or at the right side.</p>
|
||||
See {@link org.abego.treelayout.Configuration#getAlignmentInLevel() getAlignmentInLevel}.
|
||||
|
||||
|
||||
|
||||
<h4 style="page-break-before:always">Gap between Levels and Nodes</h4>
|
||||
|
||||
The gap between subsequent levels and the minimal gap between nodes can be configured.
|
||||
<p>
|
||||
<img src="doc-files/gapsAndLevels.png">
|
||||
<p>
|
||||
See {@link org.abego.treelayout.Configuration#getGapBetweenLevels(int) getGapBetweenLevels} and
|
||||
{@link org.abego.treelayout.Configuration#getGapBetweenNodes(Object, Object) getGapBetweenNodes}.
|
||||
|
||||
|
||||
<h2>Examples</h2>
|
||||
In the "demo" package you will find examples using the TreeLayout.
|
||||
<ul>
|
||||
<li>SVGDemo - Demonstrates how to use the TreeLayout to create a tree diagram with SVG (Scalable Vector Graphic)</li>
|
||||
<li>SwingDemo - Demonstrates how to use the TreeLayout to render a tree in a Swing application</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2 style="page-break-before:always">Performance</h2>
|
||||
|
||||
Based on Walker's algorithm [1] with enhancements suggested by Buchheim,
|
||||
Jünger, and Leipert [2] the software builds tree layouts in linear time.
|
||||
I.e. even trees with many nodes are built fast. Other than with the
|
||||
Reingold–Tilford algorithm [3] one is not limited to binary trees.
|
||||
|
||||
<p>
|
||||
The following figure show the results running the TreeLayout algorithm on a
|
||||
MacBook Pro 2.4 GHz Intel Core 2 Duo (2 GB Memory (-Xmx2000m)). The variously
|
||||
sized trees were created randomly.
|
||||
<p>
|
||||
<img src="doc-files/performance.png">
|
||||
<p>
|
||||
The picture illustrates the linear time behavior of the algorithm and shows
|
||||
the applicability also for large number of nodes. In this setting it takes
|
||||
approx. 5 µs to place one node.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>License</h2>
|
||||
|
||||
TreeLayout is distributed under a BSD license of
|
||||
<a href="http://www.abego-software.de">abego Software</a>.
|
||||
(<a href="doc-files/LICENSE.TXT">License text</a>)
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>Sponsor</h2>
|
||||
|
||||
The development of TreeLayout was generously sponsored by Terence Parr
|
||||
"The ANTLR Guy" (parrt at cs dot usfca dot edu).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>References</h2>
|
||||
|
||||
[1] Walker JQ II. A node-positioning algorithm for general trees.
|
||||
<i>Software—Practice and Experience</i> 1990; <b>20</b>(7):685–705.
|
||||
<p>
|
||||
[2] Buchheim C, Jünger M, Leipert S. Drawing rooted trees in linear time.
|
||||
<i>Software—Practice and Experience</i> 2006; <b>36</b>(6):651–665
|
||||
<p>
|
||||
[3] Reingold EM, Tilford JS. Tidier drawings of trees.
|
||||
<i>IEEE Transactions on Software Engineering</i> 1981; <b>7</b>(2):223–228.
|
||||
<p>
|
||||
|
||||
|
||||
@author Udo Borkowski (ub@abego.org)
|
||||
|
||||
*/
|
||||
package org.abego.treelayout;
|
||||
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.abego.treelayout.TreeForTreeLayout;
|
||||
import org.abego.treelayout.internal.util.java.lang.IterableUtil;
|
||||
import org.abego.treelayout.internal.util.java.util.ListUtil;
|
||||
|
||||
/**
|
||||
* Provides an easy way to implement the {@link TreeForTreeLayout} interface by
|
||||
* defining just two simple methods and a constructor.
|
||||
* <p>
|
||||
* To use this class the underlying tree must provide the children as a list
|
||||
* (see {@link #getChildrenList(Object)} and give direct access to the parent of
|
||||
* a node (see {@link #getParent(Object)}).
|
||||
* <p>
|
||||
*
|
||||
* See also {@link DefaultTreeForTreeLayout}.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
abstract public class AbstractTreeForTreeLayout<TreeNode> implements
|
||||
TreeForTreeLayout<TreeNode> {
|
||||
|
||||
/**
|
||||
* Returns the parent of a node, if it has one.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
*
|
||||
* @param node
|
||||
* @return [nullable] the parent of the node, or null when the node is a
|
||||
* root.
|
||||
*/
|
||||
abstract public TreeNode getParent(TreeNode node);
|
||||
|
||||
/**
|
||||
* Return the children of a node as a {@link List}.
|
||||
* <p>
|
||||
* Time Complexity: O(1)
|
||||
* <p>
|
||||
* Also the access to an item of the list must have time complexity O(1).
|
||||
* <p>
|
||||
* A client must not modify the returned list.
|
||||
*
|
||||
* @param node
|
||||
* @return the children of the given node. When node is a leaf the list is
|
||||
* empty.
|
||||
*/
|
||||
abstract public List<TreeNode> getChildrenList(TreeNode node);
|
||||
|
||||
private final TreeNode root;
|
||||
|
||||
public AbstractTreeForTreeLayout(TreeNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeNode getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf(TreeNode node) {
|
||||
return getChildrenList(node).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildOfParent(TreeNode node, TreeNode parentNode) {
|
||||
return getParent(node) == parentNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TreeNode> getChildren(TreeNode node) {
|
||||
return getChildrenList(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TreeNode> getChildrenReverse(TreeNode node) {
|
||||
return IterableUtil.createReverseIterable(getChildrenList(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeNode getFirstChild(TreeNode parentNode) {
|
||||
return getChildrenList(parentNode).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeNode getLastChild(TreeNode parentNode) {
|
||||
return ListUtil.getLast(getChildrenList(parentNode));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.util;
|
||||
|
||||
import static org.abego.treelayout.internal.util.Contract.checkArg;
|
||||
|
||||
import org.abego.treelayout.Configuration;
|
||||
|
||||
/**
|
||||
* Specify a {@link Configuration} through configurable parameters, or falling
|
||||
* back to some frequently used defaults.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
public class DefaultConfiguration<TreeNode> implements
|
||||
Configuration<TreeNode> {
|
||||
|
||||
/**
|
||||
* Specifies the constants to be used for this Configuration.
|
||||
*
|
||||
* @param gapBetweenLevels
|
||||
* @param gapBetweenNodes
|
||||
* @param location
|
||||
* [default: {@link org.abego.treelayout.Configuration.Location#Top Top}]
|
||||
* @param alignmentInLevel
|
||||
* [default: {@link org.abego.treelayout.Configuration.AlignmentInLevel#Center Center}]
|
||||
*/
|
||||
public DefaultConfiguration(double gapBetweenLevels,
|
||||
double gapBetweenNodes, Location location,
|
||||
AlignmentInLevel alignmentInLevel) {
|
||||
checkArg(gapBetweenLevels >= 0, "gapBetweenLevels must be >= 0");
|
||||
checkArg(gapBetweenNodes >= 0, "gapBetweenNodes must be >= 0");
|
||||
|
||||
this.gapBetweenLevels = gapBetweenLevels;
|
||||
this.gapBetweenNodes = gapBetweenNodes;
|
||||
this.location = location;
|
||||
this.alignmentInLevel = alignmentInLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor, using a default for the alignmentInLevel.
|
||||
* <p>
|
||||
* see
|
||||
* {@link #DefaultConfiguration(double, double, org.abego.treelayout.Configuration.Location, org.abego.treelayout.Configuration.AlignmentInLevel)}
|
||||
*/
|
||||
public DefaultConfiguration(double gapBetweenLevels,
|
||||
double gapBetweenNodes, Location location) {
|
||||
this(gapBetweenLevels, gapBetweenNodes, location,
|
||||
AlignmentInLevel.Center);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor, using a default for the rootLocation and the
|
||||
* alignmentInLevel.
|
||||
* <p>
|
||||
* see
|
||||
* {@link #DefaultConfiguration(double, double, org.abego.treelayout.Configuration.Location, org.abego.treelayout.Configuration.AlignmentInLevel)}
|
||||
*/
|
||||
public DefaultConfiguration(double gapBetweenLevels,
|
||||
double gapBetweenNodes) {
|
||||
this(gapBetweenLevels, gapBetweenNodes, Location.Top,
|
||||
AlignmentInLevel.Center);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// gapBetweenLevels
|
||||
|
||||
private final double gapBetweenLevels;
|
||||
|
||||
@Override
|
||||
public double getGapBetweenLevels(int nextLevel) {
|
||||
return gapBetweenLevels;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// gapBetweenNodes
|
||||
|
||||
private final double gapBetweenNodes;
|
||||
|
||||
@Override
|
||||
public double getGapBetweenNodes(TreeNode node1, TreeNode node2) {
|
||||
return gapBetweenNodes;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// location
|
||||
|
||||
private final Location location;
|
||||
|
||||
@Override
|
||||
public Location getRootLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// alignmentInLevel
|
||||
|
||||
private AlignmentInLevel alignmentInLevel;
|
||||
|
||||
@Override
|
||||
public AlignmentInLevel getAlignmentInLevel() {
|
||||
return alignmentInLevel;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.util;
|
||||
|
||||
import static org.abego.treelayout.internal.util.Contract.checkArg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.abego.treelayout.TreeForTreeLayout;
|
||||
|
||||
/**
|
||||
* Provides a generic implementation for the {@link TreeForTreeLayout}
|
||||
* interface, applicable to any type of tree node.
|
||||
* <p>
|
||||
* It allows you to create a tree "from scratch", without creating any new
|
||||
* class.
|
||||
* <p>
|
||||
* To create a tree you must provide the root of the tree (see
|
||||
* {@link #DefaultTreeForTreeLayout(Object)}. Then you can incrementally
|
||||
* construct the tree by adding children to the root or other nodes of the tree
|
||||
* (see {@link #addChild(Object, Object)} and
|
||||
* {@link #addChildren(Object, Object...)}).
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <TreeNode>
|
||||
*/
|
||||
public class DefaultTreeForTreeLayout<TreeNode> extends
|
||||
AbstractTreeForTreeLayout<TreeNode> {
|
||||
|
||||
private List<TreeNode> emptyList;
|
||||
|
||||
private List<TreeNode> getEmptyList() {
|
||||
if (emptyList == null) {
|
||||
emptyList = new ArrayList<TreeNode>();
|
||||
}
|
||||
return emptyList;
|
||||
}
|
||||
|
||||
private Map<TreeNode, List<TreeNode>> childrenMap = new HashMap<TreeNode, List<TreeNode>>();
|
||||
private Map<TreeNode, TreeNode> parents = new HashMap<TreeNode, TreeNode>();
|
||||
|
||||
/**
|
||||
* Creates a new instance with a given node as the root
|
||||
*
|
||||
* @param root
|
||||
* the node to be used as the root.
|
||||
*/
|
||||
public DefaultTreeForTreeLayout(TreeNode root) {
|
||||
super(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeNode getParent(TreeNode node) {
|
||||
return parents.get(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TreeNode> getChildrenList(TreeNode node) {
|
||||
List<TreeNode> result = childrenMap.get(node);
|
||||
return result == null ? getEmptyList() : result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @return true iff the node is in the tree
|
||||
*/
|
||||
public boolean hasNode(TreeNode node) {
|
||||
return node == getRoot() || parents.containsKey(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parentNode
|
||||
* [hasNode(parentNode)]
|
||||
* @param node
|
||||
* [!hasNode(node)]
|
||||
*/
|
||||
public void addChild(TreeNode parentNode, TreeNode node) {
|
||||
checkArg(hasNode(parentNode), "parentNode is not in the tree");
|
||||
checkArg(!hasNode(node), "node is already in the tree");
|
||||
|
||||
List<TreeNode> list = childrenMap.get(parentNode);
|
||||
if (list == null) {
|
||||
list = new ArrayList<TreeNode>();
|
||||
childrenMap.put(parentNode, list);
|
||||
}
|
||||
list.add(node);
|
||||
parents.put(node, parentNode);
|
||||
}
|
||||
|
||||
public void addChildren(TreeNode parentNode, TreeNode... nodes) {
|
||||
for (TreeNode node : nodes) {
|
||||
addChild(parentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.abego.treelayout.util;
|
||||
|
||||
import static org.abego.treelayout.internal.util.Contract.*;
|
||||
|
||||
import org.abego.treelayout.NodeExtentProvider;
|
||||
|
||||
/**
|
||||
* A {@link NodeExtentProvider} returning the same width and height for each
|
||||
* node.
|
||||
*
|
||||
* @author Udo Borkowski (ub@abego.org)
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class FixedNodeExtentProvider<T> implements NodeExtentProvider<T> {
|
||||
|
||||
private final double width;
|
||||
private final double height;
|
||||
|
||||
/**
|
||||
* Specifies the constants to be used as the width and height of the nodes.
|
||||
*
|
||||
* @param width
|
||||
* [default=0]
|
||||
*
|
||||
* @param height
|
||||
* [default=0]
|
||||
*/
|
||||
public FixedNodeExtentProvider(double width, double height) {
|
||||
checkArg(width >= 0, "width must be >= 0");
|
||||
checkArg(height >= 0, "height must be >= 0");
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* see {@link #FixedNodeExtentProvider(double, double)}
|
||||
*/
|
||||
public FixedNodeExtentProvider() {
|
||||
this(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWidth(T treeNode) {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHeight(T treeNode) {
|
||||
return height;
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 72 KiB |
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the abego Software GmbH nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Various utility classes to simplify usage of the
|
||||
* {@link org.abego.treelayout.TreeLayout}.
|
||||
*
|
||||
*/
|
||||
package org.abego.treelayout.util;
|
||||
|