Mercurial > hg > release > thermostat-1.6
changeset 1904:78e7dfeec5dc
Move treemap to c.r.t.client.swing.components.experimental
This patch moves treemap code not specific to the heap treemap to c.r.t.client.swing.components.experimental.
Reviewed-by: jkang
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019181.html
Original-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-August/015544.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/BreadcrumbIconResources.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.client.ui.IconDescriptor; + +class BreadcrumbIconResources { + + private static final String PACKAGE_PATH = + BreadcrumbIconResources.class.getPackage().getName().replace(".", "/"); + public static final String BREADCRUMB_HEAD = PACKAGE_PATH + "/breadcrumb_head.png"; + public static final String BREADCRUMB_BODY = PACKAGE_PATH + "/breadcrumb_body.png"; + public static final String BREADCRUMB_TAIL = PACKAGE_PATH + "/breadcrumb_tail.png"; + + private static Map<String, IconDescriptor> icons = new HashMap<>(); + + public synchronized static IconDescriptor getIcon(String path) { + if (!icons.containsKey(path)) { + try { + IconDescriptor icon = IconDescriptor.loadIcon(BreadcrumbIconResources.class.getClassLoader(), + path); + icons.put(path, icon); + + } catch (IOException ex) { + Logger.getLogger(BreadcrumbIconResources.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); + } + } + return icons.get(path); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/SquarifiedTreeMap.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,463 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + + +/** + * This class implements the Squarified algorithm for TreeMaps. Using it, it is + * possible to associate a rectangle to a {@link TreeMapNode} element and its + * children. + * <p> + * @see TreeMapNode + * @see TreMapBuilder + */ +public class SquarifiedTreeMap { + + /** + * List of node to represent as TreeMap. + */ + private LinkedList<TreeMapNode> elements; + + /** + * Represent the area in which draw nodes. + */ + private Rectangle2D.Double container; + + private enum DIRECTION { + LEFT_RIGHT, + TOP_BOTTOM + } + + /** + * Indicates the drawing direction. + */ + private DIRECTION drawingDir; + + /** + * The rectangles area available for drawing. + */ + private Rectangle2D.Double availableArea; + + /** + * List of the calculated rectangles. + */ + private List<TreeMapNode> squarifiedNodes; + + /** + * List of the current rectangles under processing. + */ + private List<TreeMapNode> currentRow; + + /** + * Coordinates on which to draw. + */ + private double lastX = 0; + private double lastY = 0; + + + /** + * Constructor. + * + * @param d the dimension of the total area in which draw elements. + * @param list the list of elements to draw as TreeMap. + * + * @throws a NullPointerException if one of the arguments is null. + */ + public SquarifiedTreeMap(Rectangle2D.Double bounds, List<TreeMapNode> list) { + this.elements = new LinkedList<>(); + elements.addAll(Objects.requireNonNull(list)); + this.container = Objects.requireNonNull(bounds); + } + + /** + * Invoke this method to calculate the rectangles for the TreeMap. + * + * @return a list of node having a rectangle built in percentage to the + * available area. + */ + public List<TreeMapNode> squarify() { + initializeArea(); + prepareData(elements); + List<TreeMapNode> row = new ArrayList<>(); + double w = getPrincipalSide(); + squarify(elements, row, w); + return getSquarifiedNodes(); + } + + /** + * Calculate recursively the rectangles to draw and their size. + * + * @param nodes the list of elements to draw. + * @param row the list of current rectangles to process. + * @param w the side against which to calculate the rectangles. + */ + private void squarify(LinkedList<TreeMapNode> nodes, List<TreeMapNode> row, double w) { + if (nodes.isEmpty() && row.isEmpty()) { + // work done + return; + } + if (nodes.isEmpty()) { + // no more element to process, just draw current row + finalizeRow(row); + return; + } + if (row.isEmpty()) { + // add the first element to the row and iterate the process over it + row.add(nodes.getFirst()); + nodes.removeFirst(); + squarify(nodes, row, w); + return; + } + + /* Greedy step: calculate the best aspect ratio of actual row and the + * best aspect ratio given by adding another rectangle to the row. + * If the current row can not be improved then finalize it + * else add the next element, to improve the global aspect ratio + */ + List<TreeMapNode> expandedRow = new ArrayList<TreeMapNode>(row); + expandedRow.add(nodes.getFirst()); + double actualAspectRatio = bestAspectRatio(row, w); + double expandedAspectRatio = bestAspectRatio(expandedRow, w); + + if (!willImprove(actualAspectRatio, expandedAspectRatio)) { + finalizeRow(row); + squarify(nodes, new ArrayList<TreeMapNode>(), getPrincipalSide()); + } else { + nodes.removeFirst(); + squarify(nodes, expandedRow, w); + } + } + + /** + * Return the rectangles list. + * @return a list of rectangles. + */ + public List<TreeMapNode> getSquarifiedNodes() { + return squarifiedNodes; + } + + /** + * Initialize the available area used to create the tree map + */ + private void initializeArea() { + availableArea = new Rectangle2D.Double(container.getX(), container.getY(), + container.getWidth(), container.getHeight()); + lastX = 0; + lastY = 0; + squarifiedNodes = new ArrayList<>(); + currentRow = new ArrayList<>(); + updateDirection(); + } + + /** + * Recalculate the drawing direction. + */ + private void updateDirection() { + drawingDir = availableArea.getWidth() > availableArea.getHeight() ? + DIRECTION.TOP_BOTTOM : DIRECTION.LEFT_RIGHT; + } + + + /** + * Invert the drawing direction. + */ + private void invertDirection() { + drawingDir = drawingDir == DIRECTION.LEFT_RIGHT ? + DIRECTION.TOP_BOTTOM : DIRECTION.LEFT_RIGHT; + } + + /** + * Keep the current list of nodes which produced the best aspect ratio + * in the available area, draw their respective rectangles and reinitialize + * the current row to draw. + * <p> + * @param nodes the list of numbers which represent the rectangles' area. + * @return the number of Rectangles created. + */ + private void finalizeRow(List<TreeMapNode> nodes) { + if (nodes == null || nodes.isEmpty()) { + return; + } + // get the total weight of nodes in order to calculate their percentages + double sum = getSum(nodes); + // greedy optimization step: get the best aspect ratio for nodes drawn + // on the longer and on the smaller side, to evaluate the best. + double actualAR = bestAspectRatio(nodes, getPrincipalSide()); + double alternativeAR = bestAspectRatio(nodes, getSecondarySide()); + + if (willImprove(actualAR, alternativeAR)) { + invertDirection(); + } + + for (TreeMapNode node: nodes) { + // assign a rectangle calculated as percentage of the total weight + Rectangle2D.Double r = createRectangle(sum, node.getWeight()); + node.setRectangle(r); + + // recalculate coordinates to draw next rectangle + updateXY(r); + + // add the node to the current list of rectangles in processing + currentRow.add(node); + } + // recalculate the area in which new rectangles will be drawn and + // reinitialize the current list of node to represent. + reduceAvailableArea(); + newRow(); + } + + + /** + * Create a rectangle having area = @param area in percentage of @param sum. + * <p> + * For example: assume @param area = 4 and @param sum = 12 and the + * drawing direction is top to bottom. <br> + * <p> + * __ __ __ __ + * | | | + * |__ __| | + * |__ __ __ __| + * + * <br>the internal rectangle will be calculated as follow:<br> + * {@code height = (4 / 9) * 3} <--note that the principal side for actual + * drawing direction is 3. + * <br>Now it is possible to calculate the width:<br> + * {@code width = 4 / 1.3} <-- note this is the height value + * + * <p> + * @param sum the total size of all rectangles in the actual row. + * @param area this Rectangle's area. + * @return the Rectangle which correctly fill the available area. + */ + private Rectangle2D.Double createRectangle(Double sum, Double area) { + double side = getPrincipalSide(); + double w = 0; + double h = 0; + + //don't want division by 0 + if (validate(area) == 0 || validate(sum) == 0 || validate(side) == 0) { + return new Rectangle2D.Double(lastX, lastY, 0, 0); + } + + // calculate the rectangle's principal side relatively to the container + // rectangle's principal side. + if (drawingDir == DIRECTION.TOP_BOTTOM) { + h = (area / sum) * side; + w = area / h; + } else { + w = (area / sum) * side; + h = area / w; + } + return new Rectangle2D.Double(lastX, lastY, w, h); + } + + /** + * Check if a double value is defined as Not a Number and sets it to 0. + * @param d the value to check. + * @return the checked value: 0 if the given number is NaN, else the number + * itself. + */ + private double validate(double d) { + if (d == Double.NaN) { + d = 0; + } + return d; + } + + /** + * Check in which direction the rectangles have to be drawn. + * @return the side on which rectangles will be created. + */ + private double getPrincipalSide() { + return drawingDir == DIRECTION.LEFT_RIGHT ? + availableArea.getWidth() : availableArea.getHeight(); + } + + /** + * + * @return the secondary available area's side. + */ + private double getSecondarySide() { + return drawingDir == DIRECTION.LEFT_RIGHT ? + availableArea.getHeight() : availableArea.getWidth(); + } + + /** + * Sum the elements in the list. + * @param nodes the list which contains elements to sum. + * @return the sum of the elements. + */ + private double getSum(List<TreeMapNode> nodes) { + double sum = 0; + for (TreeMapNode n : nodes) { + sum += n.getWeight(); + } + return sum; + } + + /** + * Recalculate the origin to draw next rectangles. + * @param r the rectangle from which recalculate the origin. + */ + private void updateXY(Rectangle2D.Double r) { + if (drawingDir == DIRECTION.LEFT_RIGHT) { + //lastY doesn't change + lastX += r.width; + } else { + //lastX doesn't change + lastY += r.height; + } + } + + /** + * Initialize the origin at the rectangle's origin. + * @param r the rectangle used as origin source. + */ + private void initializeXY(Rectangle2D.Double r) { + lastX = r.x; + lastY = r.y; + } + + /** + * Reduce the size of the available rectangle. Use it after the current + * row's closure. + */ + private void reduceAvailableArea() { + if (drawingDir == DIRECTION.LEFT_RIGHT) { + // all rectangles inside the row have the same height + availableArea.height -= currentRow.get(0).getRectangle().height; + availableArea.y = lastY + currentRow.get(0).getRectangle().height; + availableArea.x = currentRow.get(0).getRectangle().x; + } else { + // all rectangles inside the row have the same width + availableArea.width -= currentRow.get(0).getRectangle().width; + availableArea.x = lastX + currentRow.get(0).getRectangle().width; + availableArea.y = currentRow.get(0).getRectangle().y; + } + updateDirection(); + initializeXY(availableArea); + } + + /** + * Close the current row and initialize a new one. + */ + private void newRow() { + squarifiedNodes.addAll(currentRow); + currentRow = new ArrayList<>(); + } + + /** + * Calculate the aspect ratio for all the rectangles in the list and + * return the max of them. + * @param row the list of rectangles. + * @param side the side against which to calculate the the aspect ratio. + * @return the max aspect ratio calculated for the row. + */ + private double bestAspectRatio(List<TreeMapNode> row, double side) { + if (row == null || row.isEmpty()) { + return Double.MAX_VALUE; + } + double sum = getSum(row); + double max = 0; + // calculate the aspect ratio against the main side, and also its inverse. + // this is because aspect ratio of rectangle 6x4 can be calculated as + // 6/4 but also 4/6. Here the aspect ratio has been calculated as + // indicated in the Squarified algorithm. + for (TreeMapNode node : row) { + double m1 = (Math.pow(side, 2) * node.getWeight()) / Math.pow(sum, 2); + double m2 = Math.pow(sum, 2) / (Math.pow(side, 2) * node.getWeight()); + double m = Math.max(m1, m2); + + if (m > max) { + max = m; + } + } + return max; + } + + + /** + * Prepare the elements in the list, sorting them and transforming them + * proportionally the given dimension. + * @param dim the dimension in which rectangles will be drawn. + * @param elements the list of elements to draw. + * @return the list sorted and proportioned to the dimension. + */ + private void prepareData(List<TreeMapNode> elements) { + if (elements == null || elements.isEmpty()) { + return; + } + TreeMapNode.sort(elements); + double totArea = availableArea.width * availableArea.height; + double sum = getSum(elements); + + // recalculate weights in percentage of their sum + for (TreeMapNode node : elements) { + double w = (node.getWeight()/sum) * totArea; + node.setWeight(w); + } + } + + /** + * This method check which from the values in input, that represent + * rectangles' aspect ratio, produces more approximatively a square. + * It checks if one of the aspect ratio values gives a value nearest to 1 + * against the other, which means that width and height are similar. + * @param actualAR the actual aspect ratio + * @param expandedAR the aspect ratio to evaluate + * @return false if the actual aspect ratio is better than the new one, + * else true. + */ + private boolean willImprove(double actualAR, double expandedAR) { + if (actualAR == 0) { + return true; + } + if (expandedAR == 0) { + return false; + } + // check which value is closer to 1, the square's aspect ratio + double v1 = Math.abs(actualAR - 1); + double v2 = Math.abs(expandedAR - 1); + return v1 > v2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapBreadcrumb.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,305 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.Font; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.font.FontRenderContext; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Stack; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.UIManager; + +import com.redhat.thermostat.client.swing.components.Icon; + +/** + * This object creates a breadcrumb navigation bar used to trace + * {@link TreeMapComponent} objects' state. + */ +public class TreeMapBreadcrumb extends JComponent implements TreeMapObserver { + + private static final long serialVersionUID = 1L; + + /** + * Font used by bradcrumb items. + */ + private Font FONT = (Font) UIManager.get("thermostat-default-font"); + + /** + * Stack containing all items part of the breadcrumb. + */ + private Stack<BreadcrumbItem> items; + + /** + * The TreeMap object to interact with. + */ + private TreeMapComponent treemap; + + /** + * Constructor. Creates a breadcumbs navigation bar with the starting + * element and register itself as observer to the given treemap. + * + * @param start the treemap's root. + */ + public TreeMapBreadcrumb(TreeMapComponent treemap, TreeMapNode start) { + super(); + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + this.items = new Stack<>(); + this.treemap = Objects.requireNonNull(treemap); + this.treemap.register(this); + buildBreadcrumb(start); + } + + + /** + * Builds the breadcrumb using the nodes'ancestors. + * @param node the tree's branch to represent ad breadcrumb bar. + */ + public void buildBreadcrumb(TreeMapNode node) { + LinkedList<TreeMapNode> nodes = node.getAncestors(); + + while (!nodes.isEmpty()) { + BreadcrumbItem item = new BreadcrumbItem(nodes.removeLast()); + items.push(item); + add(item); + } + // the first element has no tail + items.get(0).setAsFirst(); + + //the last element has no head + items.peek().setAsLast(); + } + + + @Override + public void notifySelection(TreeMapNode node) { + // do nothing + } + + @Override + public void notifyZoomIn(TreeMapNode node) { + items.clear(); + removeAll(); + buildBreadcrumb(node); + } + + @Override + public void notifyZoomOut() { + this.remove(items.pop()); + items.peek().setAsLast(); + items.peek().repaint(); + } + + @Override + public void notifyZoomFull() { + items.clear(); + this.removeAll(); + BreadcrumbItem item = new BreadcrumbItem(treemap.getTreeMapRoot()); + item.setAsFirst(); + item.setAsLast(); + items.push(item); + this.add(item); + } + + + + /** + * This class allows to create a single item in a breadcrumb object. + * This component has 3 {@link JLabel}s which contain the images needed + * to draw an arrow. + * _____ + * > |_____| > + * | | | + * tail body head + * + */ + class BreadcrumbItem extends JComponent { + + private static final long serialVersionUID = 1L; + + private final String ROOT_TEXT = "root"; + + private JLabel tail; + private JLabel body; + private JLabel head; + + /** + * The node this items represents. + */ + private TreeMapNode node; + + /** + * The constructor creates a complete item, including both tail and head + * @param node + */ + public BreadcrumbItem(final TreeMapNode node) { + super(); + this.node = node; + this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + initComponent(); + + /** + * Simulate the click effect increasing and reducing the font size + */ + this.addMouseListener(new MouseAdapter() { + + @Override + public void mouseReleased(MouseEvent arg0) { + increaseFont(body, 2); + } + + @Override + public void mousePressed(MouseEvent arg0) { + increaseFont(body, -2); + } + + @Override + public void mouseClicked(MouseEvent arg0) { + treemap.zoomIn(node); + } + }); + } + + + /** + * Increases the given component's font size. + * @param comp the component which edit the font size to. + * @param increment value of the increment. Negative values reduce the + * font size. + */ + private void increaseFont(JComponent comp, int increment) { + Font f = comp.getFont(); + int newSize = f.getSize() + increment; + f = new Font(f.getName(), f.getStyle(), newSize); + comp.setFont(f); + comp.repaint(); + } + + private void initComponent() { + initTail(); + initBody(); + initHead(); + } + + + private void initTail() { + tail = new JLabel(); + tail.setIcon(new Icon(BreadcrumbIconResources.getIcon(BreadcrumbIconResources.BREADCRUMB_TAIL))); + this.add(tail); + } + + private void initHead() { + head = new JLabel(); + head.setIcon(new Icon(BreadcrumbIconResources.getIcon(BreadcrumbIconResources.BREADCRUMB_HEAD))); + this.add(head); + } + + private void initBody() { + body = new JLabel(); + body.setFont(FONT); + body.setHorizontalTextPosition(JLabel.CENTER); + body.setText(node.getLabel()); + adaptIcon(body, new Icon(BreadcrumbIconResources.getIcon(BreadcrumbIconResources.BREADCRUMB_BODY))); + this.add(body); + } + + + public TreeMapNode getNode() { + return this.node; + } + + /** + * Remove the tail of his breadcrumb item. + */ + public void setAsFirst() { + this.remove(tail); + this.tail = null; + this.body.setText(ROOT_TEXT); + adaptIcon(body, new Icon(BreadcrumbIconResources.getIcon(BreadcrumbIconResources.BREADCRUMB_BODY))); + } + + /** + * Remove the head of his breadcrumb item. + */ + public void setAsLast() { + this.remove(head); + this.head = null; + } + + /** + * Sets the text of this item, which is placed in the body. + * @param text + */ + public void setText(String text) { + body.setText(text); + } + + public int getHeight() { + return body.getPreferredSize().height; + } + } + + /** + * Calculates the labels' text size in order to scale the given image + * and to apply to it. + */ + private void adaptIcon(JLabel label, ImageIcon icon) { + Rectangle fontArea; + try { + fontArea = label.getFont().getStringBounds(label.getText(), + new FontRenderContext(label.getFont().getTransform(), + false, false)).getBounds(); + + } catch (NullPointerException npe) { + fontArea = label.getBounds(); + } + + Image img = icon.getImage(); + Image newimg = img.getScaledInstance(fontArea.getBounds().width + 10, + img.getHeight(null), java.awt.Image.SCALE_SMOOTH); + icon = new ImageIcon(newimg); + label.setIcon(icon); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponent.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,785 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.font.FontRenderContext; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Stack; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.border.LineBorder; + +import com.redhat.thermostat.client.swing.ThermostatSwingCursors; + +/** + * This class allows to represent a hierarchical data structure as a TreeMap. + * It extends {@link JComponent} so it can be used like usual Swing objects. + * + */ +public class TreeMapComponent extends JComponent { + + private static final long serialVersionUID = 1L; + + /** + * TreeMap's graphic root. + */ + Comp mainComp; + + /** + * Label Object to clone for faster initialization. + */ + private Label cachedLabel; + + /** + * The tree to render as TreeMap. + */ + TreeMapNode tree; + + /** + * Horizontal and vertical padding for nested component. + */ + private final int X_PADDING = TreeProcessor.X_PADDING; + private final int Y_PADDING = TreeProcessor.Y_PADDING; + + /** + * Min size for rectangles' sides. rectangles having one or both sides less + * than MIN_SIDE pixels will be not drawn. + */ + private final int MIN_SIDE = 1; + + /** + * Default value for a TreeMap component. + */ + private static final String TITLE = ""; + + /** + * TreeMap UI Constraint. + */ + public static final int SIMPLE = 0; + public static final int FLAT = 1; + public static final int ETCHED_LOWERED = 2; + public static final int ETCHED_RAISED = 3; + + /** + * Stores the chosen UI mode. + */ + private int borderStyle = ETCHED_LOWERED; + + /** + * The components' border + */ + private Border defaultBorder; + + /** + * Font and size for this component's label. + */ + private int FONT_SIZE = 8; + private Font FONT = (Font) UIManager.get("thermostat-default-font"); + + + /** + * Variable in which store last resize dimension. + */ + private Dimension lastDim; + + /** + * Variable in which store last resize event call time. + */ + private static long lastCall = 0; + + /** + * Wait time in millisec to resize the TreeMap. + */ + private final int MIN_DRAGGING_TIME = 60; + + + /** + * Stack containing the zoom calls on the TreeMap. + */ + private Stack<TreeMapNode> zoomStack; + + /** + * This object stores the last clicked rectangle in the TreeMap, in order to + * repaint it when another rectangle will be selected. + */ + private static Comp lastClicked; + + /** + * List of objects observing this. + */ + private List<TreeMapObserver> observers; + + /** + * Constructor. It draw a TreeMap of the given tree in according to the + * {@Dimension} object in input. + * + * @param tree the tree to represent as TreeMap. + * @param d the dimension the TreeMap will fulfill. + * + * @throws NullPointerException if one of the parameters is null + */ + public TreeMapComponent(TreeMapNode tree, Dimension d) { + super(); + Objects.requireNonNull(tree); + Objects.requireNonNull(d); + this.tree = tree; + lastDim = getSize(); + this.zoomStack = new Stack<>(); + this.zoomStack.push(this.tree); + this.observers = new ArrayList<>(); + + // assign a rectangle to the tree's root in order to process the tree. + Rectangle2D.Double area = new Rectangle2D.Double(0, 0, d.width, d.height); + + // calculate rectangles of tree's subtrees + TreeProcessor.processTreeMap(tree, area); + + drawTreeMap(tree); + + addResizeListener(this); + repaint(); + } + + /** + * This method returns the root of the tree showed ad TreeMap. + * @return the TreeMap's root node. + */ + public TreeMapNode getTreeMapRoot() { + return this.tree; + } + + /** + * This method is responsible for the TreeMap drawing process. + * @param tree the tree to represent as TreeMap. + */ + private void drawTreeMap(TreeMapNode tree) { + // draw root + drawMainComp(tree); + setBorderStyle(borderStyle); + + // draw subtrees nested in children + for (TreeMapNode child : tree.getChildren()) { + drawSubTree(child, mainComp); + } + // setup this component + prepareGUI(); + } + + /** + * This method prepares the layout for this component. + */ + private void prepareGUI() { + setLayout(new BorderLayout()); + setBounds(mainComp.getBounds()); + setBorder(null); + add(mainComp, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + /** + * This method prepares the main component which is the parent object where + * sub components will be placed. + * @param tree the tree's root used to prepare the main component. + */ + private void drawMainComp(TreeMapNode tree) { + mainComp = new Comp(); + mainComp.setLayout(null); + mainComp.setBounds(tree.getRectangle().getBounds()); + mainComp.setNode(tree); + cachedLabel = new Label(TITLE + tree.getLabel()); + addLabelIfPossible(TITLE + tree.getLabel(), mainComp); + } + + /** + * Create a TreeMapComp from the given node. The component is not + * instantiated as a new component but is cloned from an existing one, in + * order to improve performance. + * + * @param node the node to represent as a component. + * @return the component representing the given node. + */ + private Comp renderizeNode(TreeMapNode node) { + // if the rectangle's node is too small to be viewed, don't draw it. + if (node.getRectangle().getWidth() <= MIN_SIDE || + node.getRectangle().getHeight() <= MIN_SIDE) { + return null; + } + + Comp comp = (Comp) mainComp.clone(); + comp.setBounds(node.getRectangle().getBounds()); + + return comp; + } + + /** + * This method checks if the given container has enough space to instantiate + * a Label in it. If yes, a Label is cloned from an existing one, in order + * to improve performance. If not, it exits. + * + * @param s the label text. + * @param cont the parent container which will contain the new label. + * @return the cloned label. + */ + private Label addLabelIfPossible(String s, Container cont) { + if (s == null || s.equals("")) { + return null; + } + int componentW = cont.getSize().width; + int componentH = cont.getSize().height; + // get the rectangle associated to the area needed for the label's text + Rectangle fontArea = FONT.getStringBounds(s, + new FontRenderContext(FONT.getTransform(), + false, false)).getBounds(); + + // if the container is greater than the label, add it to the container + if (componentW > fontArea.width && componentH > fontArea.height) { + Label label = (Label) cachedLabel.clone(); + label.setBounds(5, 1, cont.getWidth(), fontArea.height); + label.setText(s); + cont.add(label); + return label; + } + return null; + } + + /** + * Draw the whole {@param tree}'s subtree inside the given component. + * @param tree the tree to draw + * @param parent the component in which build the tree. + */ + private void drawSubTree(TreeMapNode tree, JComponent parent) { + Comp comp = addCompIfPossible(tree, parent); + + // if space was enough to draw a component, try to draw its children + if (comp != null) { + comp.setNode(tree); + for (TreeMapNode child : tree.getChildren()) { + drawSubTree(child, comp); + } + } + } + + /** + * Create and add to the {@link Container} given in input a + * {@link java.awt.event.ComponentListener} listener. + * @param container the container in to assign the listener. + */ + private void addResizeListener(final Container container) { + ComponentAdapter adapter = new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + // if enough time is passed from the last call, redraw the TreeMap + if (canResize(MIN_DRAGGING_TIME)) { + Dimension newDim = container.getSize(); + + if (isChangedSize(newDim)) { + redrawTreeMap(tree); + } + } + } + }; + container.addComponentListener(adapter); + } + + /** + * This method checks if the given container has enough space to instantiate + * a TreeMapComp object in it. If yes, a Label is cloned from an existing + * one, in order to improve performance. If not, it exits. + * + * @param node the node to draw and add to the given container. + * @param cont the parent container which will contain the new component. + * @return true if the component was created and added, else false. + */ + private Comp addCompIfPossible(TreeMapNode node, Container cont) { + Rectangle2D rect = node.getRectangle(); + // if the ndoe's rectangle is smaller than the container, it is added + if (cont.getWidth() > rect.getWidth() + X_PADDING && + cont.getHeight() > rect.getHeight() + Y_PADDING) { + + Comp toReturn = renderizeNode(node); + if (toReturn == null) { + return null; + } + addLabelIfPossible(TITLE + node.getLabel(), toReturn); + + // leaves some space from the parent's origin location + Point loc = toReturn.getLocation(); + loc.x += X_PADDING; + loc.y += Y_PADDING; + toReturn.setLocation(loc); + + cont.add(toReturn); + return toReturn; + } + return null; + } + + + /** + * This method recalculates and redraws the TreeMap in according to the size + * of this component and the actual {@link TreeMapNode} object. + */ + private void redrawTreeMap(TreeMapNode newRoot) { + tree = newRoot; + Rectangle2D.Double newArea = tree.getRectangle(); + // give to the root node the size of this object so it can be recalculated + newArea.width = getSize().width; + newArea.height = getSize().height; + + // recalculate the tree + TreeProcessor.processTreeMap(tree, newArea); + + removeAll(); + drawTreeMap(tree); + } + + boolean isZoomInEnabled(TreeMapNode node) { + return !(node == null + || node.equals(this.tree) + || node.isLeaf()); + } + + public void zoomIn(TreeMapNode node) { + if (isZoomInEnabled(node)) { + fillZoomStack(node.getAncestors()); + redrawTreeMap(node); + notifyZoomInToObservers(zoomStack.peek()); + } + } + + private void fillZoomStack(LinkedList<TreeMapNode> ancestors) { + zoomStack.clear(); + while (!ancestors.isEmpty()) { + zoomStack.push(ancestors.removeLast()); + } + } + + public void zoomOut() { + // if the actual root element is not the tree's original root + if (zoomStack.size() > 1) { + zoomStack.pop(); + redrawTreeMap(zoomStack.peek()); + notifyZoomOutToObservers(); + } + } + + /** + * Zoom out the view directly to the original root. + */ + public void zoomFull() { + if (zoomStack.size() > 1) { + clearZoomCallsStack(); + redrawTreeMap(zoomStack.peek()); + notifyZoomFullToObservers(); + } + } + + /** + * Add the object in input to the list of registered objects to this TreeMap. + * @param observer the Notifiable object to register to this object. + */ + public void register(TreeMapObserver observer) { + this.observers.add(observer); + } + + /** + * Remove the object in input from the list of registered objects to this TreeMap. + * @param observer the Notifiable object to unregister from this object. + */ + public void unregister(TreeMapObserver observer) { + this.observers.remove(observer); + } + /** + * Notify observers that an object in the TreeMap has been selected. + * @param comp the selected component. + */ + private void notifySelectionToObservers(TreeMapNode node) { + for (TreeMapObserver observer : observers) { + observer.notifySelection(node); + } + } + + /** + * Notify observers that TreeMap has been zoomed. + * @param zoomedComponent + */ + private void notifyZoomInToObservers(TreeMapNode node) { + for (TreeMapObserver observer : observers) { + observer.notifyZoomIn(node); + } + } + + /** + * Notify observers that TreeMap has been zoomed. + * @param zoomedComponent + */ + private void notifyZoomOutToObservers() { + for (TreeMapObserver observer : observers) { + observer.notifyZoomOut(); + } + } + + /** + * Notify observers that TreeMap has been zoomed. + * @param zoomedComponent + */ + private void notifyZoomFullToObservers() { + for (TreeMapObserver observer : observers) { + observer.notifyZoomFull(); + } + } + + + + /** + * Returns the list of zoom operation calls. + * @return the stack that holds the zoom calls. + */ + public Stack<TreeMapNode> getZoomCallsStack() { + return zoomStack; + } + + /** + * Clear the zoom calls of this object leaving the stack with just the root. + */ + public void clearZoomCallsStack() { + while (zoomStack.size() > 1) { + zoomStack.pop(); + } + } + + /** + * check if last resize operation was called too closer to this + * one. If so, ignore it: the container is being dragged. + * + * @return true if this method is invoked at distance of + * MIN_DRAGGING_TIME millisec, else false. + */ + private boolean canResize(int millisec) { + long time = System.currentTimeMillis(); + if (time - lastCall >= millisec) { + lastCall = time; + return true; + } + return false; + } + + + /** + * Check if the dimension given in input differs from the last one stored + * by 2. + * @param newDim the new dimension to check. + * @return true if the dimensions are different, else false. + */ + private boolean isChangedSize(Dimension newDim) { + int minResizeDim = 2; + int deltaX = Math.abs(newDim.width - lastDim.width); + int deltaY = Math.abs(newDim.height - lastDim.height); + + if (deltaX > minResizeDim || deltaY > minResizeDim) { + lastDim = newDim; + return true; + } + return false; + } + + /** + * Switch the component's visualization mode to the one given in input. + * Use static constraints to set correctly a visualization mode. + * @param constraint the UI visualization mode to set. + */ + public void setBorderStyle(int UIMode) { + this.borderStyle = UIMode; + switch (borderStyle) { + case 1 : { + defaultBorder = new EmptyBorder(0, 0, 0, 0); + break; + } + case 2 : { + defaultBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED, Color.white, Color.darkGray); + break; + } + case 3 : { + defaultBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.white, Color.darkGray); + break; + } + default : { + defaultBorder = new LineBorder(Color.black, 1); + break; + } + } + applyBorderToSubtree(mainComp); + } + + /** + * Traverse recursively the tree from the given component applying to it + * the default border. + * @param comp the subtree's root from which apply the border style. + */ + private void applyBorderToSubtree(Comp comp) { + comp.setBorder(defaultBorder); + Component[] children = comp.getComponents(); + for (int i = 0; i < children.length; i++) { + if (children[i] instanceof Comp) { + applyBorderToSubtree((Comp) children[i]); + } + } + } + + /** + * Return the last clicked component inside the TreeMap. + * @return the last clicked {@Comp} object. + */ + public Comp getClickedComponent() { + return lastClicked; + } + + /** + * This class provides an extension of {@link JLabel} which main + * characteristic is to implement the {@link Cloneable} interface in order + * to make his creation faster then JLabel class. + */ + class Label extends JLabel implements Cloneable { + private static final long serialVersionUID = 1L; + + public Label(String s) { + super(s); + setFont(FONT); + setBounds(0, 0, getPreferredSize().width, FONT_SIZE); + } + + @Override + protected JLabel clone() { + Label clone = new Label(""); + clone.setFont(getFont()); + clone.setText(getText()); + clone.setBackground(getBackground()); + clone.setBounds(getBounds()); + clone.setBorder(getBorder()); + return clone; + } + } + + /** + * This class provides an extension of {@link JComponent} which main + * characteristic is to implement {@link Cloneable} interface in order to + * make his creation faster. <br> + * It also provides some action listeners that allow to select it, performing + * zoom operations for the treemap. + */ + class Comp extends JComponent implements Cloneable { + + private static final long serialVersionUID = 1L; + + /** + * The node represented by this component. + */ + private TreeMapNode node; + + /** + * The background color. It depends by the node's depth. + */ + private Color color; + + /** + * Reference to this. + */ + private Comp thisComponent; + + public Comp() { + super(); + thisComponent = this; + addClickListener(this); + addMouseListener(this); + } + + @Override + public Comp clone() { + Comp clone = new Comp(); + clone.setBounds(getBounds()); + clone.setBorder(getBorder()); + clone.setLayout(getLayout()); + clone.setOpaque(true); + return clone; + } + + public void setNode(TreeMapNode node) { + this.node = node; + this.color = node.getColor(); + ValueFormatter f = new ValueFormatter(this.node.getRealWeight()); + this.setToolTipText(this.node.getLabel() + " - " + f.format()); + } + + public TreeMapNode getNode() { + return this.node; + } + + public Color getColor() { + return this.color; + } + + public void setColor(Color c) { + this.color = c; + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (this.color != null) { + g.setColor(color); + g.fillRect(0, 0, getWidth(), getHeight()); + } + } + + /** + * Add a mouse listener to this component. It allows to select it and + * zoom it. + * @param component the component which will have the mouse listener. + */ + private void addClickListener(final JComponent component) { + MouseListener click = new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + // one left click select the rectangle + if (SwingUtilities.isLeftMouseButton(e)) { + selectComp(); + } + // double left click to zoom in (on non-leaf nodes only) + if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) { + zoomIn(getNode()); + } + // one right click to zoom out + if (SwingUtilities.isRightMouseButton(e)) { + zoomOut(); + } + // one middle click to reset zoom + if (SwingUtilities.isMiddleMouseButton(e)) { + zoomFull(); + } + } + }; + component.addMouseListener(click); + } + + /** + * Add a mouse motion listener to this component. This allows for the mouse cursor to be changed into a + * magnifying glass icon when the cursor enters a zoomable component, and back to a default cursor when it + * exits a zoomable component. + * @param component the component which will have the mouse motion listener. + */ + private void addMouseListener(final JComponent component) { + MouseListener listener = new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + if (getNode().isLeaf()) { + setDefaultCursor(); + } else { + setZoomableCursor(); + } + } + + @Override + public void mouseExited(MouseEvent e) { + if (!getNode().isLeaf()) { + setDefaultCursor(); + } else { + setZoomableCursor(); + } + } + + private void setZoomableCursor() { + component.setCursor(ThermostatSwingCursors.getZoomIconCursor()); + } + + private void setDefaultCursor() { + component.setCursor(Cursor.getDefaultCursor()); + } + }; + component.addMouseListener(listener); + } + + /** + * This method gives a darker color to this component and restore the + * original color to the last selected component. + */ + private void selectComp() { + if (lastClicked != null) { + if (!lastClicked.getNode().isLeaf()) { + lastClicked.setColor(lastClicked.getColor().brighter()); + } + lastClicked.repaint(); + } + lastClicked = thisComponent; + if (!getNode().isLeaf()) { + setColor(getColor().darker()); + } + repaint(); + notifySelectionToObservers(node); + } + } +} + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapNode.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,447 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.Color; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * This class provide a tree recursive implementation used in + * {@link SquarifiedTreeMap}. It contains a reference to the parent node and to + * a node list, which represent the node's children. It is also + * possible to store generic information inside the node using a {@link Map} + * object. Furthermore, the main property of this class is the chance to have a + * weight for the node and associate to it a {@link Rectangle2D.Double} object. + * + * <p>When an instance of this class is created, it will automatically be + * assigned a unique id. + * + * <p>By default, this class' comparator is based on the nodes' weight. + * + * <p>A static Quick Sort algorithm implementation is also provided by this + * class. + * + * @see Rectangle2D.Double + */ +public class TreeMapNode { + + /** + * Counter for assign unique id to nodes. + */ + private static int idCounter = 0; + + /** + * The rectangle which will graphically represent this node. + */ + private Rectangle2D.Double rectangle; + + /** + * This node's id. + */ + private int id; + + /** + * A Map in which store information for this node. + */ + private Map<String, String> info; + + /** + * Reference to the parent. + */ + private TreeMapNode parent; + + /** + * Reference to children. + */ + private List<TreeMapNode> children; + + /** + * The node's weight. + */ + private double weight; + + /** + * The node's label. It can be the same of another node. + */ + private String label; + + /** + * The node's weight which has been set inside the constructor. All + * operations which refers to node's weight work on the weight field, that + * is used to make calcs. + */ + private double realWeight; + + /** + * This flag indicates if weight value can be a non positive number. + */ + static boolean allowNonPositiveWeight = false; + + /** + * The color of this node. + */ + private Color color; + + /** + * Colors available on which iterate + */ + static final Color[] colors = { + Color.decode("#FACED2"), // red + Color.decode("#B9D6FF"), // blue + Color.decode("#E5E5E5"), // grey + Color.decode("#FFE7C7"), // orange + Color.decode("#ABEBEE"), // aqua + Color.decode("#E4D1FC"), // purple + Color.decode("#FFFFFF"), // white + Color.decode("#CDF9D4") // green + }; + + public final Color START_COLOR = colors[0]; + + /** + * + * Constructor that allow to set the nodes' real weight. Others fields are + * initialized to their default value. + * It automatically set the node's id. + * + * <p> + * @param realWeight the nodes real weight, which will be not affected + * during node processing. + * + */ + public TreeMapNode(double realWeight) { + this("", realWeight); + } + + /** + * + * Constructor that allow to set the nodes' real weight and the label. + * Others fields are initialized to their default value. + * It automatically set the node's id. + * + * <p> + * @param label the node's label. + * @param realWeight the nodes real weight, which will be not affected + * during node processing. + * + */ + public TreeMapNode(String label, double realWeight) { + this.id = idCounter++; + this.label = label; + this.parent = null; + this.children = new ArrayList<TreeMapNode>(); + this.rectangle = new Rectangle2D.Double(); + this.info = new HashMap<String, String>(); + this.weight = realWeight; + this.realWeight = realWeight; + } + + /** + * Return the id of this object. + * @return the id automatically assigned at this object initialization. + */ + public int getId() { + return this.id; + } + + /** + * Set this node's label. + * @param newLabel the new label to set. + */ + public void setLabel(String newLabel) { + this.label = newLabel; + } + + /** + * Return the label of this object. + * @return the label assigned at instantiation time to this object. + */ + public String getLabel() { + return this.label; + } + + /** + * Return the reference to the node parent of this object. + * @return the parent of this node. It can be null. + */ + public TreeMapNode getParent() { + return this.parent; + } + + /** + * Set as parent of this object the node given in input. + * @param parent the new parent of this object. No checks are made for null + * value. + */ + public void setParent(TreeMapNode parent) { + this.parent = parent; + } + + /** + * Return the list of nodes representing this node's children. + * @return a list of {@link TreeMapNode} objects. + */ + public List<TreeMapNode> getChildren() { + return this.children; + } + + public boolean isLeaf() { + return getChildren().isEmpty(); + } + + /** + * Set as children list of this object the list given in input. + * @param children the new list of children for this node. + */ + public void setChildren(List<TreeMapNode> children) { + this.children = children; + for (TreeMapNode child : this.children) { + child.setParent(this); + } + } + + /** + * Return the {@link Map} object containing all information of this node. + * @return a {@link Map} object. + */ + public Map<String, String> getInfo() { + return this.info; + } + + /** + * Store the given information into this object. + * @param key the key searching value for the information to store. + * @param value the information to store into this object. + * @return the old value for the given key. + */ + public String addInfo(String key, String value) { + return this.info.put(key, value); + } + + /** + * Return the information stored in this object, corresponding to the key + * given in input. + * @param key the key value for the search information. + * @return the corresponding value for the given key. + */ + public String getInfo(String key) { + return this.info.get(key); + } + + /** + * Add the object given in input to the children list of this object. It + * also add this object as its parent. + * @param child the new child to add at this object. + */ + public void addChild(TreeMapNode child) { + if (child != null) { + this.children.add(child); + child.setParent(this); + } + } + + @Override + /** + * Return a {@link String} representing this object. + */ + public String toString() { + return getClass().getSimpleName() + " [" + "label = " + getLabel() + + "; weight =" + getRealWeight() + + "; rectangle=" + rectangle.getBounds() + "]"; + } + + /** + * Return the weight of this object. In case of allowNonPositiveWeight is + * set to false and the weight is 0, less than 0 or not a number + * ({@link Double.Nan}), this method returns a value that can be transformed + * by external objects, so if you need the real weight you have to + * invoke getrealWeight(). + * + * @return the node's weight. + */ + public double getWeight() { + if ((weight <= 0 || weight == Double.NaN) && !allowNonPositiveWeight) { + return realWeight; + } + return this.weight; + } + + /** + * Use this method to retrieve the real weight assigned to this node. + * @return the weight corresponding to this node. + */ + public double getRealWeight() { + return this.realWeight; + } + + + /** + * Use this method to set the real weight of this node. + */ + public void setRealWeight(double w) { + this.realWeight = w; + } + + /** + * Set the weight of this object. If a negative value is given, it is set + * automatically to 0. + * @param weight the new weight for this object. + */ + public void setWeight(double w) { + this.weight = w < 0 && !allowNonPositiveWeight ? 0 : w; + } + + + /** + * Return the rectangle representing this object. + * @return a {@link Rectangle2D.Double} object. + */ + public Rectangle2D.Double getRectangle() { + if (this.rectangle == null) { + throw new RuntimeException(); + } + return this.rectangle; + } + + /** + * Set a new rectangle for this object. + * @param rectangle the new rectangle that represent this node. + */ + public void setRectangle(Rectangle2D.Double rectangle) { + this.rectangle = rectangle; + } + + /** + * + * @return true if non positive value can be used as weight, else false. + */ + public static boolean isAllowNonPositiveWeight() { + return allowNonPositiveWeight; + } + + /** + * Set this value to false and nodes will be not able to manage non positive + * values for weight field, otherwise set to true. + * @param allowed the flag value for managing non positive values as weight + */ + public static void setAllowNonPositiveWeight(boolean allowed) { + allowNonPositiveWeight = allowed; + } + + /** + * This method assess if the rectangle associated to this node is drawable, + * which means that its sides are greater than 1. + * @return true if the rectangle associated to this node is drawable, + * else false. + */ + public boolean isDrawable() { + if (rectangle.width >= 1 && rectangle.height >= 1) { + return true; + } + return false; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + /** + * Returns this node's next color. + * @return the color which came after this node's color in the color list. + * If this node has no color assigned then the START_COLOR is returned. + */ + public Color getNextColor() { + if (this.color != null) { + for (int i = 0; i < colors.length; i++) { + if (this.color.equals(colors[i])) { + return colors[(i + 1) % colors.length]; + } + } + } + return START_COLOR; + } + + + public int getDepth() { + if (this.parent == null) { + return 0; + } else { + return 1 + parent.getDepth(); + } + } + + /** + * This method sorts the given list in <b>descending<b> way. + * + * @param nodes the list of {@link TreeMapNode} to sort. + */ + public static void sort(List<TreeMapNode> nodes) { + Comparator<TreeMapNode> c = new Comparator<TreeMapNode>() { + @Override + public int compare(TreeMapNode o1, TreeMapNode o2) { + // inverting the result to descending sort the list + return -(Double.compare(o1.getWeight(), o2.getWeight())); + } + }; + Collections.sort(nodes, c); + } + + /** + * Return the list of ancestors node of this object. The first one is this + * node itself, the last one the root. + * @return a list of ancestors nodes. + */ + public LinkedList<TreeMapNode> getAncestors() { + LinkedList<TreeMapNode> toReturn = new LinkedList<TreeMapNode>(); + TreeMapNode tmp = this; + do { + toReturn.add(tmp); + } while ((tmp = tmp.getParent()) != null); + return toReturn; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapObserver.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +/** + * This interface is used as part of the Observer Design Pattern developed + * for objects who want to be notified about TreeMap's events. + */ +public interface TreeMapObserver { + + /** + * This method inform the Observer object that the object passed as + * argument has been selected. + * + * @param selectedComp the selected component to communicate to + * this Observer object. + */ + public void notifySelection(TreeMapNode node); + + /** + * This method informs objects that a zoom in event has been performed on + * the given node. + * @param node the zoomed node. + */ + public void notifyZoomIn(TreeMapNode node); + + /** + * This method informs objects that a zoom out event has been performed. + */ + public void notifyZoomOut(); + + /** + * This method informs objects that the zoom level has been resetted. + */ + public void notifyZoomFull(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapToolbar.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,124 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.Objects; + +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; + +/** + * This class provides a tool bar containing a {@lnk TreeMapBreadcrumb} object + * and a {@link TreeMapZoomBar} instance to control the state of + * {@link TreeMapComponent} objects. + */ +@SuppressWarnings("serial") +public class TreeMapToolbar extends JComponent { + + /** + * The panel in which objects are placed. + */ + private JPanel contentPane; + + /** + * The scroll pane used to hide breadcrumb's first items. + */ + private JScrollPane scrollPane; + + + public TreeMapToolbar(TreeMapComponent treemap) { + super(); + initComponent(Objects.requireNonNull(treemap)); + } + + + private void initComponent(TreeMapComponent treemap) { + this.setLayout(new BorderLayout()); + + final JPanel breadcrumbPanel = new JPanel(); + breadcrumbPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); + add(breadcrumbPanel, BorderLayout.CENTER); + + JPanel zoomPanel = new JPanel(new FlowLayout()); + // add some empty space between the breadcrumb bar and buttons + zoomPanel.add(Box.createHorizontalStrut(20)); + add(zoomPanel, BorderLayout.EAST); + + TreeMapZoomBar zoomBar = new TreeMapZoomBar(treemap); + zoomPanel.add(zoomBar); + + TreeMapBreadcrumb bc = new TreeMapBreadcrumb(treemap, treemap.getTreeMapRoot()); + + contentPane = new JPanel(new FlowLayout(FlowLayout.LEFT)); + contentPane.add(bc); + + scrollPane = new JScrollPane(contentPane); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + scrollPane.setBorder(null); + + // allows to see always the last elements of the breadcrumb. + scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() { + public void adjustmentValueChanged(AdjustmentEvent e) { + e.getAdjustable().setValue(e.getAdjustable().getMaximum()); + } + }); + breadcrumbPanel.add(scrollPane); + + // when the component is resized the new dimension is used to arrange + // the scrollpane, in order to use all available space. + breadcrumbPanel.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent arg0) { + Dimension d = breadcrumbPanel.getSize(); + d.height = 20; + scrollPane.setPreferredSize(d); + } + }); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapZoomBar.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,242 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Objects; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import com.redhat.thermostat.client.swing.components.FontAwesomeIcon; +import com.redhat.thermostat.client.swing.components.Icon; +import com.redhat.thermostat.client.swing.internal.LocaleResources; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.shared.locale.Translate; + +/** + * This class provides a component containing zoom in/out/full buttons which can + * control a {@link TreeMapComponent} object. + */ +public class TreeMapZoomBar extends JComponent implements TreeMapObserver { + + private static final long serialVersionUID = 1L; + + private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + + private JLabel zoomOut; + private JLabel zoomFull; + private JLabel zoomIn; + + private Color defaultColor = Palette.BLACK.getColor(); + private Color enterColor = Palette.THERMOSTAT_BLU.getColor(); + + /** + * The treemap object to interact with. + */ + private TreeMapComponent treemap; + + /** + * If an item is selected in the treemap, it is stored in order to zoom on + * it if the ZoomIn button is pressed. + */ + private TreeMapNode selectedItem; + + /** + * Constructor. It creates the zoom buttons and registers this object as + * treemap observer. + */ + public TreeMapZoomBar(TreeMapComponent treemap) { + super(); + this.treemap = Objects.requireNonNull(treemap); + initComponent(); + treemap.register(this); + } + + private void initComponent() { + this.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); + createZoomInButton(); + createZoomFullButton(); + createZoomOutButton(); + + /* + * At the beginning no actions can be performed: + * Cannot zoom in because no item is selected; + * cannot zoom out because zoom in hasn't been performed; + * the same for zoom full. + */ + zoomIn.setEnabled(false); + zoomFull.setEnabled(false); + zoomOut.setEnabled(false); + } + + + + private void createZoomInButton() { + zoomIn = new JLabel(); + final Icon baseIcon = new FontAwesomeIcon('\uf065', 15, defaultColor); + final Icon hoverIcon = new FontAwesomeIcon('\uf065', 15, enterColor); + + zoomIn.setIcon(baseIcon); + zoomIn.setToolTipText(t.localize(LocaleResources.TREEMAP_ZOOM_IN).getContents()); + + zoomIn.addMouseListener(new MouseAdapter() { + @Override + public void mouseExited(MouseEvent e) { + zoomIn.setIcon(baseIcon); + } + + @Override + public void mouseEntered(MouseEvent e) { + zoomIn.setIcon(hoverIcon); + } + + @Override + public void mouseClicked(MouseEvent arg0) { + if (selectedItem != null) { + treemap.zoomIn(selectedItem); + } + } + }); + + this.add(zoomIn); + } + + private void createZoomOutButton() { + zoomOut = new JLabel(); + + final Icon baseIcon = new FontAwesomeIcon('\uf066', 15, defaultColor); + final Icon hoverIcon = new FontAwesomeIcon('\uf066', 15, enterColor); + + zoomOut.setIcon(baseIcon); + zoomOut.setToolTipText(t.localize(LocaleResources.TREEMAP_ZOOM_OUT).getContents()); + zoomOut.addMouseListener(new MouseAdapter() { + @Override + public void mouseExited(MouseEvent e) { + zoomOut.setIcon(baseIcon); + } + + @Override + public void mouseEntered(MouseEvent e) { + zoomOut.setIcon(hoverIcon); + } + + @Override + public void mouseClicked(MouseEvent arg0) { + treemap.zoomOut(); + } + }); + + this.add(zoomOut); + } + + private void createZoomFullButton() { + zoomFull = new JLabel(); + + final Icon baseIcon = new FontAwesomeIcon('\uf03b', 15, defaultColor); + final Icon hoverIcon = new FontAwesomeIcon('\uf03b', 15, enterColor); + + zoomFull.setIcon(baseIcon); + zoomFull.setToolTipText(t.localize(LocaleResources.TREEMAP_ZOOM_FULL).getContents()); + zoomFull.addMouseListener(new MouseAdapter() { + @Override + public void mouseExited(MouseEvent e) { + zoomFull.setIcon(baseIcon); + } + + @Override + public void mouseEntered(MouseEvent e) { + zoomFull.setIcon(hoverIcon); + } + + @Override + public void mouseClicked(MouseEvent arg0) { + treemap.zoomFull(); + } + }); + + this.add(zoomFull); + } + + /** + * Changes the buttons state in according to the treemap view. + */ + private void changeState() { + selectedItem = null; + zoomIn.setEnabled(false); + + if (!isRootShown()) { + zoomFull.setEnabled(true); + zoomOut.setEnabled(true); + } else { + zoomFull.setEnabled(false); + zoomOut.setEnabled(false); + } + } + + @Override + public void notifySelection(TreeMapNode node) { + selectedItem = node; + zoomIn.setEnabled(treemap.isZoomInEnabled(node)); + } + + @Override + public void notifyZoomIn(TreeMapNode node) { + changeState(); + } + + @Override + public void notifyZoomOut() { + changeState(); + } + + @Override + public void notifyZoomFull() { + // no actions can be performed + zoomFull.setEnabled(false); + zoomOut.setEnabled(false); + zoomIn.setEnabled(false); + } + + private boolean isRootShown() { + return treemap.getTreeMapRoot() == treemap.getZoomCallsStack().firstElement(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeProcessor.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,111 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.awt.Color; +import java.awt.geom.Rectangle2D; +import java.util.Objects; + + +public class TreeProcessor { + + /** + * Padding between the main component and its sub component. + */ + public static final int X_PADDING = 15; + public static final int Y_PADDING = 20; + + /** + * This method process recursively the tree nested in the node element + * passed as argument in the constructor calculating the children TreeMap + * for each node also applying coloring. + * @return the updated tree, where nodes have additional information like + * {@link Rectangle2D>Float} instance and a color. + */ + public static TreeMapNode processTreeMap(TreeMapNode tree, Rectangle2D.Double area) { + Objects.requireNonNull(tree); + Objects.requireNonNull(area); + tree.setRectangle(area); + if (tree.getColor() == null) { + tree.setColor(tree.START_COLOR); + } + + process(tree); + return tree; + } + + /** + * This method is used to effectively process the whole tree structure. It + * uses a {@link SquarifiedTreeMap} object to calculate a TreeMap for each + * node who has children. + * @param node the subtree's root to process + */ + private static void process(TreeMapNode node) { + + SquarifiedTreeMap algorithm = new SquarifiedTreeMap(getSubArea(node.getRectangle()), node.getChildren()); + node.setChildren(algorithm.squarify()); + + Color c = node.getNextColor(); + + for (TreeMapNode child : node.getChildren()) { + //children will have all the same color, which is the parent's next one + if (child.getColor() == null) { + child.setColor(c); + } + // if squarified rectangles have drawable sides then continue to + // process, else don't process the subtree having as root a + // non drawable rectangle. + if (child.isDrawable()) { + process(child); + } + } + } + + /** + * Calculate space and coordinates in which children's rectangle will be + * drawn, from the main component. + * @return the rectangle representing the new available area. + */ + private static Rectangle2D.Double getSubArea(Rectangle2D.Double area) { + Rectangle2D.Double subArea = new Rectangle2D.Double(); + subArea.setRect(area); + + subArea.width = Math.max(0, (subArea.width - 2 * X_PADDING)); + subArea.height = Math.max(0, (subArea.height - 1.5 * Y_PADDING)); + return subArea; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/ValueFormatter.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,72 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +public class ValueFormatter { + + private double value; + + public ValueFormatter(double val) { + this.value = val; + } + + /** + * This method returns the node value calculating it in bytes, KB or MB. + * + * i.e. if node's weight = 200 it returns: "200 bytes" <br> + * if weight = 20152: "20.15 KB" <br> + * if weight = 2015248: "2.01 MB" <br> + * + * Note that float values are approximated to the second decimal digit. + */ + public String format() { + int KB = 1000; + int MB = 1000000; + String unit = "Bytes"; + + if (value >= KB && value < MB) { + value /= KB; + unit = "KBytes"; + } else if (value >= MB) { + value /= MB; + unit = "MBytes"; + } + // show 2 decimal digits + String formattedValue = String.format("%.2f", value); + return formattedValue + " " + unit; + } +}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/LocaleResources.java Wed Jun 01 15:58:04 2016 -0400 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/LocaleResources.java Mon Jun 06 15:17:50 2016 -0400 @@ -47,6 +47,10 @@ ZOOM_OUT, RESET_ZOOM, + TREEMAP_ZOOM_IN, + TREEMAP_ZOOM_OUT, + TREEMAP_ZOOM_FULL, + CUT, COPY, PASTE,
Binary file client/swing/src/main/resources/com/redhat/thermostat/client/swing/components/experimental/breadcrumb_body.png has changed
Binary file client/swing/src/main/resources/com/redhat/thermostat/client/swing/components/experimental/breadcrumb_head.png has changed
Binary file client/swing/src/main/resources/com/redhat/thermostat/client/swing/components/experimental/breadcrumb_tail.png has changed
--- a/client/swing/src/main/resources/com/redhat/thermostat/client/swing/internal/strings.properties Wed Jun 01 15:58:04 2016 -0400 +++ b/client/swing/src/main/resources/com/redhat/thermostat/client/swing/internal/strings.properties Mon Jun 06 15:17:50 2016 -0400 @@ -5,6 +5,10 @@ ZOOM_OUT = Zoom Out RESET_ZOOM = Reset Zoom +TREEMAP_ZOOM_IN = Zoom in the selected item. Try also with a double click +TREEMAP_ZOOM_OUT = Zoom out the view. Try also with a left click +TREEMAP_ZOOM_FULL = Restore the original zoom level. Try also with a mouse wheel click + CUT = Cut COPY = Copy PASTE = Paste \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/SquarifiedTreeMapTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,118 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +/** + * Using eclEmma tool has been proved that this test covers 100% + * of {@link SquarifiedTreeMap} code and also 90% of {@link TreeMapBuilder} code. + */ +public class SquarifiedTreeMapTest { + + private SquarifiedTreeMap algorithm; + Rectangle2D.Double bounds; + List<TreeMapNode> list; + + @Before + public void setUp() throws Exception { + bounds = new Rectangle2D.Double(0, 0, 10, 5); + list = new ArrayList<>(); + } + + @Test + public final void testSquarifiedTreeMap() { + //check every parameters combinations + boolean catched = false; + try { + algorithm = new SquarifiedTreeMap(null, null); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + catched = false; + + try { + algorithm = new SquarifiedTreeMap(bounds, null); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + catched = false; + + try { + algorithm = new SquarifiedTreeMap(null, list); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + } + + @Test + public final void testSquarify() { + // test using an empty node list + algorithm = new SquarifiedTreeMap(bounds, new ArrayList<TreeMapNode>()); + assertEquals(0, algorithm.squarify().size()); + + // test using a correct list + int n = 10; + for (int i = 0; i < n; i++) { + list.add(new TreeMapNode(i+1)); + } + // process the list + algorithm = new SquarifiedTreeMap(bounds, list); + list = algorithm.squarify(); + + assertEquals(n, list.size()); + + for (int i = 0; i < n; i++) { + // node has been processed + assertNotNull(list.get(i).getRectangle()); + } + + assertEquals(list, algorithm.getSquarifiedNodes()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponentTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,293 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import junit.framework.Assert; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.awt.Dimension; +import java.lang.reflect.InvocationTargetException; + +import javax.swing.SwingUtilities; + +import org.junit.BeforeClass; +import org.junit.Test; + + +public class TreeMapComponentTest { + + private TreeMapComponent treeMap; + private static TreeMapNode tree; + private static TreeMapNode node1; + private static TreeMapNode node2; + private static Dimension dim; + + @BeforeClass + public static void setUpOnce() { + tree = new TreeMapNode(1); + node1 = new TreeMapNode(1); + node2 = new TreeMapNode(1); + tree.addChild(node1); + node1.addChild(node2); + dim = new Dimension(500, 500); + } + + + @Test + public final void testTreeMapComponent() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + + boolean catched = false; + + try { + treeMap = new TreeMapComponent(tree, dim); + // pass + } catch(NullPointerException e) { + Assert.fail("Didn't expect exception."); + } + try { + treeMap = new TreeMapComponent(null, null); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + catched = false; + + try { + treeMap = new TreeMapComponent(tree, null); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + catched = false; + + try { + treeMap = new TreeMapComponent(null, dim); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + } + }); + } + + @Test + public final void testGetRoot() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + treeMap = new TreeMapComponent(tree, dim); + assertEquals(tree, treeMap.getTreeMapRoot()); + } + }); + } + + @Test + public final void testIsZoomInEnabled() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + TreeMapComponent treeMap = new TreeMapComponent(tree, dim); + + assertFalse("Should not be able to zoom in on null", treeMap.isZoomInEnabled(null)); + assertFalse("Should not be able to zoom in on root", treeMap.isZoomInEnabled(tree)); + assertTrue("Should be able to zoom in on node 1", treeMap.isZoomInEnabled(node1)); + assertFalse("Should not be able to zoom in on node 2", treeMap.isZoomInEnabled(node2)); + } + }); + } + + @Test + public final void testZoomIn() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + TreeMapComponent treeMap = new TreeMapComponent(tree, dim); + + treeMap.zoomIn(node1); + assertEquals(node1, treeMap.getTreeMapRoot()); + + treeMap.zoomIn(node2); + assertEquals(node1, treeMap.getTreeMapRoot()); + } + }); + } + + @Test + public final void testZoomOut() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + treeMap = new TreeMapComponent(tree, dim); + + treeMap.zoomOut(); + assertEquals(tree, treeMap.getTreeMapRoot()); + + treeMap.zoomIn(node1); //if zoom out root is tree + treeMap.zoomIn(node2); //no-op, cannot zoom on leaf + + assertEquals(node1, treeMap.getTreeMapRoot()); + + treeMap.zoomOut(); + assertEquals(tree, treeMap.getTreeMapRoot()); + } + }); + } + + @Test + public final void testZoomFull() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + treeMap = new TreeMapComponent(tree, dim); + + treeMap.zoomIn(node2); + treeMap.zoomFull(); + assertEquals(tree, treeMap.getTreeMapRoot()); + + } + }); + } + + @Test + public final void testGetZoomCallsStack() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + treeMap = new TreeMapComponent(tree, dim); + + // the root is always in the stack + assertEquals(1, treeMap.getZoomCallsStack().size()); + + treeMap.zoomIn(tree); + // zooming on the same element nothing happen + assertEquals(1, treeMap.getZoomCallsStack().size()); + + treeMap.zoomIn(node1); + treeMap.zoomIn(node2); + treeMap.zoomFull(); + assertEquals(tree, treeMap.getTreeMapRoot()); + } + }); + } + + + @Test + public final void testClearZoomCallsStack() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + treeMap = new TreeMapComponent(tree, dim); + + treeMap.clearZoomCallsStack(); + assertEquals(1, treeMap.getZoomCallsStack().size()); + + treeMap.zoomIn(node1); + treeMap.zoomIn(node2); + treeMap.clearZoomCallsStack(); + assertEquals(1, treeMap.getZoomCallsStack().size()); + } + }); + } + + @Test + public final void testObserver() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + boolean zoomedIn = false; + boolean zoomedOut = false; + boolean zoomedFull = false; + + TreeMapObserver observer = new TreeMapObserver() { + @Override + public void notifyZoomOut() { + zoomedOut = true; + } + + @Override + public void notifyZoomIn(TreeMapNode node) { + zoomedIn = true; + } + + @Override + public void notifyZoomFull() { + zoomedFull = true; + } + + @Override + public void notifySelection(TreeMapNode node) { + } + }; + + @Override + public void run() { + TreeMapNode child = new TreeMapNode(1); + tree.addChild(child); + TreeMapNode grandchild = new TreeMapNode(1); + child.addChild(grandchild); + + treeMap = new TreeMapComponent(tree, dim); + treeMap.register(observer); + + treeMap.zoomIn(child); + assertTrue("Should have zoomed in on child", zoomedIn); + zoomedIn = false; + + treeMap.zoomIn(grandchild); + assertFalse("Should not have zoomed in on grandchild", zoomedIn); + + treeMap.zoomOut(); + assertTrue("Should have zoomed out", zoomedOut); + + treeMap.zoomIn(child); + treeMap.zoomFull(); + assertTrue("Should have zoomed full", zoomedFull); + } + }); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapNodeTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,288 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.awt.Color; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +public class TreeMapNodeTest { + + private TreeMapNode node; + private static final double DELTA = 0.001; + + @Before + public void setUp() { + node = new TreeMapNode(null, 1); + } + + @Test + public final void testGetId() { + TreeMapNode node1 = new TreeMapNode(null, 1); + TreeMapNode node2 = new TreeMapNode(null, 1); + assertTrue(node1.getId() != node2.getId()); + assertTrue(node1.getId() + 1 == node2.getId()); + } + + @Test + public final void testGetSetParent() { + TreeMapNode parent = new TreeMapNode(null, 1); + assertTrue(node.getParent() == null); + node.setParent(parent); + assertTrue(node.getParent() == parent); + } + + @Test + public final void testGetSetLabel() { + TreeMapNode node = new TreeMapNode("MyLabel", 1); + assertTrue(node.getLabel().equals("MyLabel")); + node.setLabel("MyNewLabel"); + assertTrue(node.getLabel().equals("MyNewLabel")); + } + + @Test + public final void testGetSetChildren() { + assertTrue(node.getChildren().isEmpty()); + + TreeMapNode node = new TreeMapNode(null, 1); + List<TreeMapNode> children = new ArrayList<>(); + children.add(node); + + node.setChildren(children); + assertTrue(1 == node.getChildren().size()); + } + + @Test + public final void testIsLeaf() { + assertTrue(node.isLeaf()); + node.addChild(new TreeMapNode(null, 1)); + assertFalse(node.isLeaf()); + node.setChildren(Collections.<TreeMapNode>emptyList()); + assertTrue(node.isLeaf()); + } + + @Test + public final void testGetAddInfo() { + node.addInfo("exampleKey", "exampleValue"); + assertEquals("exampleValue", node.getInfo("exampleKey")); + } + + @Test + public final void testAddChild() { + assertTrue(node.getChildren().size() == 0); + node.addChild(new TreeMapNode(null, 1)); + assertTrue(node.getChildren().size() == 1); + + node.addChild(null); + assertTrue(node.getChildren().size() == 1); // null has not been added + } + + @Test + public final void testGetSetWeight() { + assertEquals(1.0, node.getWeight(), DELTA); + node.setWeight(5); + assertEquals(5.0, node.getWeight(), DELTA); + } + + @Test + public final void testGetSetRectangle() { + Rectangle2D.Double r = new Rectangle2D.Double(5, 5, 5, 5); + node.setRectangle(r); + assertEquals(r, node.getRectangle()); + + node.setRectangle(null); + boolean catched = false; + try { + node.getRectangle(); + } catch(RuntimeException e) { + catched = true; + } + assertTrue(catched); + } + + + @Test + public final void testGetSetRealWeight() { + node = new TreeMapNode(null, 5); + assertEquals(5.0, node.getRealWeight(), DELTA); + node.setRealWeight(8); + assertEquals(8.0, node.getRealWeight(), DELTA); + } + + @Test + public final void testAllowNonPositiveWeight() { + assertFalse(TreeMapNode.isAllowNonPositiveWeight()); + node.setWeight(-5); + assertEquals(1.0, node.getWeight(), DELTA); + assertEquals(1.0, node.getRealWeight(), DELTA); + + TreeMapNode.setAllowNonPositiveWeight(true); + node.setWeight(-5); + assertEquals(-5.0, node.getWeight(), DELTA); + } + + @Test + public final void testIsDrawable() { + Rectangle2D.Double r = new Rectangle2D.Double(5, 5, 5, 5); + node.setRectangle(r); + assertTrue(node.isDrawable()); + + r.setRect(0, 0, 0.5f, 0.5f); + assertFalse(node.isDrawable()); + + r.setRect(0, 0, 5f, 0.5f); + assertFalse(node.isDrawable()); + + r.setRect(0, 0, 0.5f, 5f); + assertFalse(node.isDrawable()); + } + + @Test + public final void testGetSetColor() { + assertNull(node.getColor()); + node.setColor(Color.black); + assertEquals(Color.black, node.getColor()); + } + + @Test + public final void testGetDepth() { + TreeMapNode depth1 = new TreeMapNode(null, 1); + TreeMapNode depth2 = new TreeMapNode(null, 1); + + node.addChild(depth1); + depth1.addChild(depth2); + + assertTrue(node.getDepth() == 0); + assertTrue(depth1.getDepth() == 1); + assertTrue(depth2.getDepth() == 2); + } + + @Test + public final void testSort() { + + TreeMapNode n1 = new TreeMapNode(null, 5); + TreeMapNode n2 = new TreeMapNode(null, 4); + TreeMapNode n4 = new TreeMapNode(null, 2); + TreeMapNode n3 = new TreeMapNode(null, 3); + TreeMapNode n5 = new TreeMapNode(null, 0); + TreeMapNode n6 = new TreeMapNode(null, 7); + TreeMapNode n7 = new TreeMapNode(null, 1); + TreeMapNode n8 = new TreeMapNode(null, 9); + + List<TreeMapNode> toSort = new ArrayList<>(); + toSort.add(n3); + toSort.add(n2); + toSort.add(n4); + toSort.add(n1); + toSort.add(n5); + toSort.add(n6); + toSort.add(n7); + toSort.add(n8); + + TreeMapNode.sort(toSort); + + assertEquals(toSort.get(0), n8); + assertEquals(toSort.get(1), n6); + assertEquals(toSort.get(2), n1); + assertEquals(toSort.get(3), n2); + assertEquals(toSort.get(4), n3); + assertEquals(toSort.get(5), n4); + assertEquals(toSort.get(6), n7); + assertEquals(toSort.get(7), n5); + } + + @Test + public final void testGetInfo() { + Map<String, String> map = node.getInfo(); + assertNotNull(map); + assertEquals(0, map.keySet().size()); + } + + @Test + public final void testToString() { + assertNotNull(node.toString()); + } + + + @Test + public final void testGetNextColor() { + assertNull(node.getColor()); + assertTrue(node.getNextColor().equals(node.START_COLOR)); + + Color start = node.START_COLOR; + node.setColor(start); + + for (int i = 0; i < TreeMapNode.colors.length; i++) { + assertEquals(TreeMapNode.colors[i], node.getColor()); + node.setColor(node.getNextColor()); + } + } + + + @Test + public final void testGetAncestors() { + TreeMapNode node1 = new TreeMapNode(0); + TreeMapNode node2 = new TreeMapNode(0); + TreeMapNode node3 = new TreeMapNode(0); + TreeMapNode node4 = new TreeMapNode(0); + + node1.addChild(node2); + node2.addChild(node3); + node3.addChild(node4); + + LinkedList<TreeMapNode> ancestors = node4.getAncestors(); + + assertEquals(node1, ancestors.get(3)); + assertEquals(node2, ancestors.get(2)); + assertEquals(node3, ancestors.get(1)); + assertEquals(node4, ancestors.get(0)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapToolbarTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import junit.framework.Assert; + +import static org.junit.Assert.assertTrue; + +import java.awt.Dimension; +import java.lang.reflect.InvocationTargetException; + +import javax.swing.SwingUtilities; + +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import com.redhat.thermostat.annotations.internal.CacioTest; + +@Category(CacioTest.class) +public class TreeMapToolbarTest { + + private TreeMapComponent treeMap; + @SuppressWarnings("unused") + private TreeMapToolbar toolbar; + + private static TreeMapNode tree; + private static Dimension dim; + + @Test + public final void testTreeMapToolbar() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + + tree = new TreeMapNode(1); + dim = new Dimension(500, 500); + treeMap = new TreeMapComponent(tree, dim); + + boolean catched = false; + try { + toolbar = new TreeMapToolbar(null); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + try { + toolbar = new TreeMapToolbar(treeMap); + } catch (NullPointerException e) { + Assert.fail("Should not throw any exception."); + } + } + }); + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapZoomBarTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,94 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import junit.framework.Assert; + +import static org.junit.Assert.assertTrue; + +import java.awt.Dimension; +import java.lang.reflect.InvocationTargetException; + +import javax.swing.SwingUtilities; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import com.redhat.thermostat.annotations.internal.CacioTest; + +@Category(CacioTest.class) +public class TreeMapZoomBarTest { + + private TreeMapComponent treeMap; + @SuppressWarnings("unused") + private TreeMapZoomBar zoomBar; + + private static TreeMapNode tree; + private static Dimension dim; + + @Before + public void setUp() { + tree = new TreeMapNode(1); + dim = new Dimension(500, 500); + } + + @Test + public final void testTreeMapZoomBar() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + boolean catched = false; + try { + zoomBar = new TreeMapZoomBar(null); + } catch(NullPointerException e) { + catched = true; + } + assertTrue(catched); + try { + treeMap = new TreeMapComponent(tree, dim); + zoomBar = new TreeMapZoomBar(treeMap); + } catch (NullPointerException e) { + Assert.fail("Should not throw any exception."); + } + } + }); + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeProcessorTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,165 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.awt.geom.Rectangle2D; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class TreeProcessorTest { + + private static final double DELTA = 0.1; + private static final double BASE = 2.0; + + TreeMapNode node; + Rectangle2D.Double area; + + @Before + public void setUp() throws Exception { + node = new TreeMapNode(1); + area = new Rectangle2D.Double(0, 0, 500, 500); + } + + @Test + public final void testTreeProcessor() { + boolean caught = false; + // this test check all wrong combinations for constructor parameters + try { + TreeProcessor.processTreeMap(null, area); + } catch(NullPointerException e) { + caught = true; + } + assertTrue(caught); + caught = false; + + try { + TreeProcessor.processTreeMap(node, null); + } catch(NullPointerException e) { + caught = true; + } + assertTrue(caught); + caught = false; + + try { + TreeProcessor.processTreeMap(null, null); + } catch(NullPointerException e) { + caught = true; + } + assertTrue(caught); + } + + + @Test + public final void testProcessTreeMapProcessesWholeTree() { + generateTree(node, 5, 5); + TreeProcessor.processTreeMap(node, area); + + // the test will check if any drawable node in the tree has a rectangle and a + // color, which means the processor function has processed the whole tree + traverse(node); + } + + private void traverse(TreeMapNode tree) { + if (tree.isDrawable() && (tree.getRectangle() == null || tree.getColor() == null)) { + fail("node " + tree.getId() + " not processed"); + } + for (TreeMapNode child : tree.getChildren()) { + traverse(child); + } + } + + private void generateTree(TreeMapNode root, int levels, int childrenNumber) { + if (levels == 0) { + return; + } else { + for (int i = 0; i < childrenNumber; i++) { + root.addChild(new TreeMapNode(100)); + } + for (TreeMapNode child : root.getChildren()) { + generateTree(child, levels-1, childrenNumber); + } + } + } + + @Test + public final void testProcessTreeMapNodeSizing() { + final int numSiblings = 5; + final double originalDimension = 64.0; + final double smallerDimension = 32.0; + + generateSiblingTree(node, numSiblings); + + TreeProcessor.processTreeMap(node, new Rectangle2D.Double(0, 0, originalDimension, originalDimension)); + checkNodeWeightRatios(numSiblings); + + //now resize smaller + TreeProcessor.processTreeMap(node, new Rectangle2D.Double(0, 0, smallerDimension, smallerDimension)); + + //now resize back to original size + TreeProcessor.processTreeMap(node, new Rectangle2D.Double(0, 0, originalDimension, originalDimension)); + //if the first call to checkNodeWeightRatios(numSiblings) worked, this should too + checkNodeWeightRatios(numSiblings); + } + + private void generateSiblingTree(TreeMapNode root, int numSiblings) { + assertTrue(numSiblings > 1); + root.addChild(new TreeMapNode(1.0)); //this is not a random weight + for(int i = 0; i < (numSiblings - 1); i++) { + root.addChild(new TreeMapNode(Math.pow(BASE, i))); + } + } + + private void checkNodeWeightRatios(int numSiblings) { + List<TreeMapNode> children = node.getChildren(); + assertEquals(numSiblings, children.size()); + TreeMapNode.sort(children); + + double weight = children.get(0).getWeight(); + for(int i = 1; i < numSiblings - 1; i++) { + double currentWeight = children.get(i).getWeight(); + //check that the ratio between node weights is approximately 2 (which is the BASE) + assertEquals(BASE, weight/currentWeight, DELTA); + weight = currentWeight; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/ValueFormatterTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class ValueFormatterTest { + + private ValueFormatter formatter; + + @Test + public final void getFormattedWeight() { + formatter = new ValueFormatter(2); + assertEquals("2.00 Bytes", formatter.format()); + + formatter = new ValueFormatter(2222); + assertEquals("2.22 KBytes", formatter.format()); + + formatter = new ValueFormatter(2222222); + assertEquals("2.22 MBytes", formatter.format()); + } +} \ No newline at end of file
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapIconResources.java Wed Jun 01 15:58:04 2016 -0400 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapIconResources.java Mon Jun 06 15:17:50 2016 -0400 @@ -36,22 +36,20 @@ package com.redhat.thermostat.vm.heap.analysis.client.core; -import com.redhat.thermostat.client.ui.IconDescriptor; - import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import com.redhat.thermostat.client.ui.IconDescriptor; + public class HeapIconResources { - public static final String PIN_MASK = "com/redhat/thermostat/vm/heap/analysis/client/core/pin_mask.png"; - public static final String TRIGGER_HEAP_DUMP = "com/redhat/thermostat/vm/heap/analysis/client/core/take_dump.png"; - - public static final String BREADCRUMB_HEAD = "com/redhat/thermostat/vm/heap/analysis/client/swing/breadcrumb_head.png"; - public static final String BREADCRUMB_BODY = "com/redhat/thermostat/vm/heap/analysis/client/swing/breadcrumb_body.png"; - public static final String BREADCRUMB_TAIL = "com/redhat/thermostat/vm/heap/analysis/client/swing/breadcrumb_tail.png"; + private static final String PACKAGE_PATH = + HeapIconResources.class.getPackage().getName().replace(".", "/"); + public static final String PIN_MASK = PACKAGE_PATH + "/pin_mask.png"; + public static final String TRIGGER_HEAP_DUMP = PACKAGE_PATH + "/take_dump.png"; private static Map<String, IconDescriptor> icons = new HashMap<>();
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/locale/LocaleResources.java Wed Jun 01 15:58:04 2016 -0400 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/locale/LocaleResources.java Mon Jun 06 15:17:50 2016 -0400 @@ -79,10 +79,6 @@ DUMPS_LIST, LIST_DUMPS_ACTION, HEAP_DUMP_SECTION_TREEMAP, - - ZOOM_IN, - ZOOM_OUT, - ZOOM_FULL, ; static final String RESOURCE_BUNDLE = "com.redhat.thermostat.vm.heap.analysis.client.locale.strings";
--- a/vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/locale/strings.properties Wed Jun 01 15:58:04 2016 -0400 +++ b/vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/locale/strings.properties Mon Jun 06 15:17:50 2016 -0400 @@ -37,7 +37,3 @@ HEAP_DUMP_IN_PROGRESS = Dumping heap\u2026 HEAP_DUMP_LOADING_IN_PROGRESS = Loading heap dump\u2026 PROCESS_EXITED = Process exited. - -ZOOM_IN = Zoom in the selected item. Try also with a double click -ZOOM_OUT = Zoom out the view. Try also with a left click -ZOOM_FULL = Restore the original zoom level. Try also with a mouse wheel click
Binary file vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/swing/breadcrumb_body.png has changed
Binary file vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/swing/breadcrumb_head.png has changed
Binary file vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/swing/breadcrumb_tail.png has changed
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HistogramConverter.java Wed Jun 01 15:58:04 2016 -0400 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HistogramConverter.java Mon Jun 06 15:17:50 2016 -0400 @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.List; +import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; import com.redhat.thermostat.common.utils.DescriptorConverter; import com.redhat.thermostat.vm.heap.analysis.common.HistogramRecord; import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram;
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SquarifiedTreeMap.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,463 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; - - -/** - * This class implements the Squarified algorithm for TreeMaps. Using it, it is - * possible to associate a rectangle to a {@link TreeMapNode} element and its - * children. - * <p> - * @see TreeMapNode - * @see TreMapBuilder - */ -public class SquarifiedTreeMap { - - /** - * List of node to represent as TreeMap. - */ - private LinkedList<TreeMapNode> elements; - - /** - * Represent the area in which draw nodes. - */ - private Rectangle2D.Double container; - - private enum DIRECTION { - LEFT_RIGHT, - TOP_BOTTOM - } - - /** - * Indicates the drawing direction. - */ - private DIRECTION drawingDir; - - /** - * The rectangles area available for drawing. - */ - private Rectangle2D.Double availableArea; - - /** - * List of the calculated rectangles. - */ - private List<TreeMapNode> squarifiedNodes; - - /** - * List of the current rectangles under processing. - */ - private List<TreeMapNode> currentRow; - - /** - * Coordinates on which to draw. - */ - private double lastX = 0; - private double lastY = 0; - - - /** - * Constructor. - * - * @param d the dimension of the total area in which draw elements. - * @param list the list of elements to draw as TreeMap. - * - * @throws a NullPointerException if one of the arguments is null. - */ - public SquarifiedTreeMap(Rectangle2D.Double bounds, List<TreeMapNode> list) { - this.elements = new LinkedList<>(); - elements.addAll(Objects.requireNonNull(list)); - this.container = Objects.requireNonNull(bounds); - } - - /** - * Invoke this method to calculate the rectangles for the TreeMap. - * - * @return a list of node having a rectangle built in percentage to the - * available area. - */ - public List<TreeMapNode> squarify() { - initializeArea(); - prepareData(elements); - List<TreeMapNode> row = new ArrayList<>(); - double w = getPrincipalSide(); - squarify(elements, row, w); - return getSquarifiedNodes(); - } - - /** - * Calculate recursively the rectangles to draw and their size. - * - * @param nodes the list of elements to draw. - * @param row the list of current rectangles to process. - * @param w the side against which to calculate the rectangles. - */ - private void squarify(LinkedList<TreeMapNode> nodes, List<TreeMapNode> row, double w) { - if (nodes.isEmpty() && row.isEmpty()) { - // work done - return; - } - if (nodes.isEmpty()) { - // no more element to process, just draw current row - finalizeRow(row); - return; - } - if (row.isEmpty()) { - // add the first element to the row and iterate the process over it - row.add(nodes.getFirst()); - nodes.removeFirst(); - squarify(nodes, row, w); - return; - } - - /* Greedy step: calculate the best aspect ratio of actual row and the - * best aspect ratio given by adding another rectangle to the row. - * If the current row can not be improved then finalize it - * else add the next element, to improve the global aspect ratio - */ - List<TreeMapNode> expandedRow = new ArrayList<TreeMapNode>(row); - expandedRow.add(nodes.getFirst()); - double actualAspectRatio = bestAspectRatio(row, w); - double expandedAspectRatio = bestAspectRatio(expandedRow, w); - - if (!willImprove(actualAspectRatio, expandedAspectRatio)) { - finalizeRow(row); - squarify(nodes, new ArrayList<TreeMapNode>(), getPrincipalSide()); - } else { - nodes.removeFirst(); - squarify(nodes, expandedRow, w); - } - } - - /** - * Return the rectangles list. - * @return a list of rectangles. - */ - public List<TreeMapNode> getSquarifiedNodes() { - return squarifiedNodes; - } - - /** - * Initialize the available area used to create the tree map - */ - private void initializeArea() { - availableArea = new Rectangle2D.Double(container.getX(), container.getY(), - container.getWidth(), container.getHeight()); - lastX = 0; - lastY = 0; - squarifiedNodes = new ArrayList<>(); - currentRow = new ArrayList<>(); - updateDirection(); - } - - /** - * Recalculate the drawing direction. - */ - private void updateDirection() { - drawingDir = availableArea.getWidth() > availableArea.getHeight() ? - DIRECTION.TOP_BOTTOM : DIRECTION.LEFT_RIGHT; - } - - - /** - * Invert the drawing direction. - */ - private void invertDirection() { - drawingDir = drawingDir == DIRECTION.LEFT_RIGHT ? - DIRECTION.TOP_BOTTOM : DIRECTION.LEFT_RIGHT; - } - - /** - * Keep the current list of nodes which produced the best aspect ratio - * in the available area, draw their respective rectangles and reinitialize - * the current row to draw. - * <p> - * @param nodes the list of numbers which represent the rectangles' area. - * @return the number of Rectangles created. - */ - private void finalizeRow(List<TreeMapNode> nodes) { - if (nodes == null || nodes.isEmpty()) { - return; - } - // get the total weight of nodes in order to calculate their percentages - double sum = getSum(nodes); - // greedy optimization step: get the best aspect ratio for nodes drawn - // on the longer and on the smaller side, to evaluate the best. - double actualAR = bestAspectRatio(nodes, getPrincipalSide()); - double alternativeAR = bestAspectRatio(nodes, getSecondarySide()); - - if (willImprove(actualAR, alternativeAR)) { - invertDirection(); - } - - for (TreeMapNode node: nodes) { - // assign a rectangle calculated as percentage of the total weight - Rectangle2D.Double r = createRectangle(sum, node.getWeight()); - node.setRectangle(r); - - // recalculate coordinates to draw next rectangle - updateXY(r); - - // add the node to the current list of rectangles in processing - currentRow.add(node); - } - // recalculate the area in which new rectangles will be drawn and - // reinitialize the current list of node to represent. - reduceAvailableArea(); - newRow(); - } - - - /** - * Create a rectangle having area = @param area in percentage of @param sum. - * <p> - * For example: assume @param area = 4 and @param sum = 12 and the - * drawing direction is top to bottom. <br> - * <p> - * __ __ __ __ - * | | | - * |__ __| | - * |__ __ __ __| - * - * <br>the internal rectangle will be calculated as follow:<br> - * {@code height = (4 / 9) * 3} <--note that the principal side for actual - * drawing direction is 3. - * <br>Now it is possible to calculate the width:<br> - * {@code width = 4 / 1.3} <-- note this is the height value - * - * <p> - * @param sum the total size of all rectangles in the actual row. - * @param area this Rectangle's area. - * @return the Rectangle which correctly fill the available area. - */ - private Rectangle2D.Double createRectangle(Double sum, Double area) { - double side = getPrincipalSide(); - double w = 0; - double h = 0; - - //don't want division by 0 - if (validate(area) == 0 || validate(sum) == 0 || validate(side) == 0) { - return new Rectangle2D.Double(lastX, lastY, 0, 0); - } - - // calculate the rectangle's principal side relatively to the container - // rectangle's principal side. - if (drawingDir == DIRECTION.TOP_BOTTOM) { - h = (area / sum) * side; - w = area / h; - } else { - w = (area / sum) * side; - h = area / w; - } - return new Rectangle2D.Double(lastX, lastY, w, h); - } - - /** - * Check if a double value is defined as Not a Number and sets it to 0. - * @param d the value to check. - * @return the checked value: 0 if the given number is NaN, else the number - * itself. - */ - private double validate(double d) { - if (d == Double.NaN) { - d = 0; - } - return d; - } - - /** - * Check in which direction the rectangles have to be drawn. - * @return the side on which rectangles will be created. - */ - private double getPrincipalSide() { - return drawingDir == DIRECTION.LEFT_RIGHT ? - availableArea.getWidth() : availableArea.getHeight(); - } - - /** - * - * @return the secondary available area's side. - */ - private double getSecondarySide() { - return drawingDir == DIRECTION.LEFT_RIGHT ? - availableArea.getHeight() : availableArea.getWidth(); - } - - /** - * Sum the elements in the list. - * @param nodes the list which contains elements to sum. - * @return the sum of the elements. - */ - private double getSum(List<TreeMapNode> nodes) { - double sum = 0; - for (TreeMapNode n : nodes) { - sum += n.getWeight(); - } - return sum; - } - - /** - * Recalculate the origin to draw next rectangles. - * @param r the rectangle from which recalculate the origin. - */ - private void updateXY(Rectangle2D.Double r) { - if (drawingDir == DIRECTION.LEFT_RIGHT) { - //lastY doesn't change - lastX += r.width; - } else { - //lastX doesn't change - lastY += r.height; - } - } - - /** - * Initialize the origin at the rectangle's origin. - * @param r the rectangle used as origin source. - */ - private void initializeXY(Rectangle2D.Double r) { - lastX = r.x; - lastY = r.y; - } - - /** - * Reduce the size of the available rectangle. Use it after the current - * row's closure. - */ - private void reduceAvailableArea() { - if (drawingDir == DIRECTION.LEFT_RIGHT) { - // all rectangles inside the row have the same height - availableArea.height -= currentRow.get(0).getRectangle().height; - availableArea.y = lastY + currentRow.get(0).getRectangle().height; - availableArea.x = currentRow.get(0).getRectangle().x; - } else { - // all rectangles inside the row have the same width - availableArea.width -= currentRow.get(0).getRectangle().width; - availableArea.x = lastX + currentRow.get(0).getRectangle().width; - availableArea.y = currentRow.get(0).getRectangle().y; - } - updateDirection(); - initializeXY(availableArea); - } - - /** - * Close the current row and initialize a new one. - */ - private void newRow() { - squarifiedNodes.addAll(currentRow); - currentRow = new ArrayList<>(); - } - - /** - * Calculate the aspect ratio for all the rectangles in the list and - * return the max of them. - * @param row the list of rectangles. - * @param side the side against which to calculate the the aspect ratio. - * @return the max aspect ratio calculated for the row. - */ - private double bestAspectRatio(List<TreeMapNode> row, double side) { - if (row == null || row.isEmpty()) { - return Double.MAX_VALUE; - } - double sum = getSum(row); - double max = 0; - // calculate the aspect ratio against the main side, and also its inverse. - // this is because aspect ratio of rectangle 6x4 can be calculated as - // 6/4 but also 4/6. Here the aspect ratio has been calculated as - // indicated in the Squarified algorithm. - for (TreeMapNode node : row) { - double m1 = (Math.pow(side, 2) * node.getWeight()) / Math.pow(sum, 2); - double m2 = Math.pow(sum, 2) / (Math.pow(side, 2) * node.getWeight()); - double m = Math.max(m1, m2); - - if (m > max) { - max = m; - } - } - return max; - } - - - /** - * Prepare the elements in the list, sorting them and transforming them - * proportionally the given dimension. - * @param dim the dimension in which rectangles will be drawn. - * @param elements the list of elements to draw. - * @return the list sorted and proportioned to the dimension. - */ - private void prepareData(List<TreeMapNode> elements) { - if (elements == null || elements.isEmpty()) { - return; - } - TreeMapNode.sort(elements); - double totArea = availableArea.width * availableArea.height; - double sum = getSum(elements); - - // recalculate weights in percentage of their sum - for (TreeMapNode node : elements) { - double w = (node.getWeight()/sum) * totArea; - node.setWeight(w); - } - } - - /** - * This method check which from the values in input, that represent - * rectangles' aspect ratio, produces more approximatively a square. - * It checks if one of the aspect ratio values gives a value nearest to 1 - * against the other, which means that width and height are similar. - * @param actualAR the actual aspect ratio - * @param expandedAR the aspect ratio to evaluate - * @return false if the actual aspect ratio is better than the new one, - * else true. - */ - private boolean willImprove(double actualAR, double expandedAR) { - if (actualAR == 0) { - return true; - } - if (expandedAR == 0) { - return false; - } - // check which value is closer to 1, the square's aspect ratio - double v1 = Math.abs(actualAR - 1); - double v2 = Math.abs(expandedAR - 1); - return v1 > v2; - } -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapBreadcrumb.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,306 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.Font; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.font.FontRenderContext; -import java.util.LinkedList; -import java.util.Objects; -import java.util.Stack; - -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.UIManager; - -import com.redhat.thermostat.client.swing.components.Icon; -import com.redhat.thermostat.vm.heap.analysis.client.core.HeapIconResources; - -/** - * This object creates a breadcrumb navigation bar used to trace - * {@link TreeMapComponent} objects' state. - */ -public class TreeMapBreadcrumb extends JComponent implements TreeMapObserver { - - private static final long serialVersionUID = 1L; - - /** - * Font used by bradcrumb items. - */ - private Font FONT = (Font) UIManager.get("thermostat-default-font"); - - /** - * Stack containing all items part of the breadcrumb. - */ - private Stack<BreadcrumbItem> items; - - /** - * The TreeMap object to interact with. - */ - private TreeMapComponent treemap; - - /** - * Constructor. Creates a breadcumbs navigation bar with the starting - * element and register itself as observer to the given treemap. - * - * @param start the treemap's root. - */ - public TreeMapBreadcrumb(TreeMapComponent treemap, TreeMapNode start) { - super(); - setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - this.items = new Stack<>(); - this.treemap = Objects.requireNonNull(treemap); - this.treemap.register(this); - buildBreadcrumb(start); - } - - - /** - * Builds the breadcrumb using the nodes'ancestors. - * @param node the tree's branch to represent ad breadcrumb bar. - */ - public void buildBreadcrumb(TreeMapNode node) { - LinkedList<TreeMapNode> nodes = node.getAncestors(); - - while (!nodes.isEmpty()) { - BreadcrumbItem item = new BreadcrumbItem(nodes.removeLast()); - items.push(item); - add(item); - } - // the first element has no tail - items.get(0).setAsFirst(); - - //the last element has no head - items.peek().setAsLast(); - } - - - @Override - public void notifySelection(TreeMapNode node) { - // do nothing - } - - @Override - public void notifyZoomIn(TreeMapNode node) { - items.clear(); - removeAll(); - buildBreadcrumb(node); - } - - @Override - public void notifyZoomOut() { - this.remove(items.pop()); - items.peek().setAsLast(); - items.peek().repaint(); - } - - @Override - public void notifyZoomFull() { - items.clear(); - this.removeAll(); - BreadcrumbItem item = new BreadcrumbItem(treemap.getTreeMapRoot()); - item.setAsFirst(); - item.setAsLast(); - items.push(item); - this.add(item); - } - - - - /** - * This class allows to create a single item in a breadcrumb object. - * This component has 3 {@link JLabel}s which contain the images needed - * to draw an arrow. - * _____ - * > |_____| > - * | | | - * tail body head - * - */ - class BreadcrumbItem extends JComponent { - - private static final long serialVersionUID = 1L; - - private final String ROOT_TEXT = "root"; - - private JLabel tail; - private JLabel body; - private JLabel head; - - /** - * The node this items represents. - */ - private TreeMapNode node; - - /** - * The constructor creates a complete item, including both tail and head - * @param node - */ - public BreadcrumbItem(final TreeMapNode node) { - super(); - this.node = node; - this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - initComponent(); - - /** - * Simulate the click effect increasing and reducing the font size - */ - this.addMouseListener(new MouseAdapter() { - - @Override - public void mouseReleased(MouseEvent arg0) { - increaseFont(body, 2); - } - - @Override - public void mousePressed(MouseEvent arg0) { - increaseFont(body, -2); - } - - @Override - public void mouseClicked(MouseEvent arg0) { - treemap.zoomIn(node); - } - }); - } - - - /** - * Increases the given component's font size. - * @param comp the component which edit the font size to. - * @param increment value of the increment. Negative values reduce the - * font size. - */ - private void increaseFont(JComponent comp, int increment) { - Font f = comp.getFont(); - int newSize = f.getSize() + increment; - f = new Font(f.getName(), f.getStyle(), newSize); - comp.setFont(f); - comp.repaint(); - } - - private void initComponent() { - initTail(); - initBody(); - initHead(); - } - - - private void initTail() { - tail = new JLabel(); - tail.setIcon(new Icon(HeapIconResources.getIcon(HeapIconResources.BREADCRUMB_TAIL))); - this.add(tail); - } - - private void initHead() { - head = new JLabel(); - head.setIcon(new Icon(HeapIconResources.getIcon(HeapIconResources.BREADCRUMB_HEAD))); - this.add(head); - } - - private void initBody() { - body = new JLabel(); - body.setFont(FONT); - body.setHorizontalTextPosition(JLabel.CENTER); - body.setText(node.getLabel()); - adaptIcon(body, new Icon(HeapIconResources.getIcon(HeapIconResources.BREADCRUMB_BODY))); - this.add(body); - } - - - public TreeMapNode getNode() { - return this.node; - } - - /** - * Remove the tail of his breadcrumb item. - */ - public void setAsFirst() { - this.remove(tail); - this.tail = null; - this.body.setText(ROOT_TEXT); - adaptIcon(body, new Icon(HeapIconResources.getIcon(HeapIconResources.BREADCRUMB_BODY))); - } - - /** - * Remove the head of his breadcrumb item. - */ - public void setAsLast() { - this.remove(head); - this.head = null; - } - - /** - * Sets the text of this item, which is placed in the body. - * @param text - */ - public void setText(String text) { - body.setText(text); - } - - public int getHeight() { - return body.getPreferredSize().height; - } - } - - /** - * Calculates the labels' text size in order to scale the given image - * and to apply to it. - */ - private void adaptIcon(JLabel label, ImageIcon icon) { - Rectangle fontArea; - try { - fontArea = label.getFont().getStringBounds(label.getText(), - new FontRenderContext(label.getFont().getTransform(), - false, false)).getBounds(); - - } catch (NullPointerException npe) { - fontArea = label.getBounds(); - } - - Image img = icon.getImage(); - Image newimg = img.getScaledInstance(fontArea.getBounds().width + 10, - img.getHeight(null), java.awt.Image.SCALE_SMOOTH); - icon = new ImageIcon(newimg); - label.setIcon(icon); - } -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapComponent.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,794 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.font.FontRenderContext; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Stack; - -import javax.swing.BorderFactory; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.border.EtchedBorder; -import javax.swing.border.LineBorder; - -import com.redhat.thermostat.client.swing.ThermostatSwingCursors; -import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; - -/** - * This class allows to represent a hierarchical data structure as a TreeMap. - * It extends {@link JComponent} so it can be used like usual Swing objects. - * - */ -public class TreeMapComponent extends JComponent { - - private static final long serialVersionUID = 1L; - - /** - * TreeMap's graphic root. - */ - Comp mainComp; - - /** - * Label Object to clone for faster initialization. - */ - private Label cachedLabel; - - /** - * The tree to render as TreeMap. - */ - TreeMapNode tree; - - /** - * Horizontal and vertical padding for nested component. - */ - private final int X_PADDING = TreeProcessor.X_PADDING; - private final int Y_PADDING = TreeProcessor.Y_PADDING; - - /** - * Min size for rectangles' sides. rectangles having one or both sides less - * than MIN_SIDE pixels will be not drawn. - */ - private final int MIN_SIDE = 1; - - /** - * Default value for a TreeMap component. - */ - private static final String TITLE = ""; - - /** - * TreeMap UI Constraint. - */ - public static final int SIMPLE = 0; - public static final int FLAT = 1; - public static final int ETCHED_LOWERED = 2; - public static final int ETCHED_RAISED = 3; - - /** - * Stores the chosen UI mode. - */ - private int borderStyle = ETCHED_LOWERED; - - /** - * The components' border - */ - private Border defaultBorder; - - /** - * Font and size for this component's label. - */ - private int FONT_SIZE = 8; - private Font FONT = (Font) UIManager.get("thermostat-default-font"); - - - /** - * Variable in which store last resize dimension. - */ - private Dimension lastDim; - - /** - * Variable in which store last resize event call time. - */ - private static long lastCall = 0; - - /** - * Wait time in millisec to resize the TreeMap. - */ - private final int MIN_DRAGGING_TIME = 60; - - - /** - * Stack containing the zoom calls on the TreeMap. - */ - private Stack<TreeMapNode> zoomStack; - - /** - * This object stores the last clicked rectangle in the TreeMap, in order to - * repaint it when another rectangle will be selected. - */ - private static Comp lastClicked; - - /** - * List of objects observing this. - */ - private List<TreeMapObserver> observers; - - /** - * Constructor which creates a TreeMapComponent by an histogram object. - * @param histogram the histogram to represent as tree map. - */ - public TreeMapComponent(ObjectHistogram histogram) { - this(HistogramConverter.convertToTreeMap(histogram), new Dimension()); - } - - /** - * Constructor. It draw a TreeMap of the given tree in according to the - * {@Dimension} object in input. - * - * @param tree the tree to represent as TreeMap. - * @param d the dimension the TreeMap will fulfill. - * - * @throws NullPointerException if one of the parameters is null - */ - public TreeMapComponent(TreeMapNode tree, Dimension d) { - super(); - Objects.requireNonNull(tree); - Objects.requireNonNull(d); - this.tree = tree; - lastDim = getSize(); - this.zoomStack = new Stack<>(); - this.zoomStack.push(this.tree); - this.observers = new ArrayList<>(); - - // assign a rectangle to the tree's root in order to process the tree. - Rectangle2D.Double area = new Rectangle2D.Double(0, 0, d.width, d.height); - - // calculate rectangles of tree's subtrees - TreeProcessor.processTreeMap(tree, area); - - drawTreeMap(tree); - - addResizeListener(this); - repaint(); - } - - /** - * This method returns the root of the tree showed ad TreeMap. - * @return the TreeMap's root node. - */ - public TreeMapNode getTreeMapRoot() { - return this.tree; - } - - /** - * This method is responsible for the TreeMap drawing process. - * @param tree the tree to represent as TreeMap. - */ - private void drawTreeMap(TreeMapNode tree) { - // draw root - drawMainComp(tree); - setBorderStyle(borderStyle); - - // draw subtrees nested in children - for (TreeMapNode child : tree.getChildren()) { - drawSubTree(child, mainComp); - } - // setup this component - prepareGUI(); - } - - /** - * This method prepares the layout for this component. - */ - private void prepareGUI() { - setLayout(new BorderLayout()); - setBounds(mainComp.getBounds()); - setBorder(null); - add(mainComp, BorderLayout.CENTER); - revalidate(); - repaint(); - } - - /** - * This method prepares the main component which is the parent object where - * sub components will be placed. - * @param tree the tree's root used to prepare the main component. - */ - private void drawMainComp(TreeMapNode tree) { - mainComp = new Comp(); - mainComp.setLayout(null); - mainComp.setBounds(tree.getRectangle().getBounds()); - mainComp.setNode(tree); - cachedLabel = new Label(TITLE + tree.getLabel()); - addLabelIfPossible(TITLE + tree.getLabel(), mainComp); - } - - /** - * Create a TreeMapComp from the given node. The component is not - * instantiated as a new component but is cloned from an existing one, in - * order to improve performance. - * - * @param node the node to represent as a component. - * @return the component representing the given node. - */ - private Comp renderizeNode(TreeMapNode node) { - // if the rectangle's node is too small to be viewed, don't draw it. - if (node.getRectangle().getWidth() <= MIN_SIDE || - node.getRectangle().getHeight() <= MIN_SIDE) { - return null; - } - - Comp comp = (Comp) mainComp.clone(); - comp.setBounds(node.getRectangle().getBounds()); - - return comp; - } - - /** - * This method checks if the given container has enough space to instantiate - * a Label in it. If yes, a Label is cloned from an existing one, in order - * to improve performance. If not, it exits. - * - * @param s the label text. - * @param cont the parent container which will contain the new label. - * @return the cloned label. - */ - private Label addLabelIfPossible(String s, Container cont) { - if (s == null || s.equals("")) { - return null; - } - int componentW = cont.getSize().width; - int componentH = cont.getSize().height; - // get the rectangle associated to the area needed for the label's text - Rectangle fontArea = FONT.getStringBounds(s, - new FontRenderContext(FONT.getTransform(), - false, false)).getBounds(); - - // if the container is greater than the label, add it to the container - if (componentW > fontArea.width && componentH > fontArea.height) { - Label label = (Label) cachedLabel.clone(); - label.setBounds(5, 1, cont.getWidth(), fontArea.height); - label.setText(s); - cont.add(label); - return label; - } - return null; - } - - /** - * Draw the whole {@param tree}'s subtree inside the given component. - * @param tree the tree to draw - * @param parent the component in which build the tree. - */ - private void drawSubTree(TreeMapNode tree, JComponent parent) { - Comp comp = addCompIfPossible(tree, parent); - - // if space was enough to draw a component, try to draw its children - if (comp != null) { - comp.setNode(tree); - for (TreeMapNode child : tree.getChildren()) { - drawSubTree(child, comp); - } - } - } - - /** - * Create and add to the {@link Container} given in input a - * {@link ComponentResized} listener. - * @param c the container in to assign the listener. - */ - private void addResizeListener(final Container container) { - ComponentAdapter adapter = new ComponentAdapter() { - public void componentResized(ComponentEvent e) { - // if enough time is passed from the last call, redraw the TreeMap - if (canResize(MIN_DRAGGING_TIME)) { - Dimension newDim = container.getSize(); - - if (isChangedSize(newDim)) { - redrawTreeMap(tree); - } - } - } - }; - container.addComponentListener(adapter); - } - - /** - * This method checks if the given container has enough space to instantiate - * a TreeMapComp object in it. If yes, a Label is cloned from an existing - * one, in order to improve performance. If not, it exits. - * - * @param node the node to draw and add to the given container. - * @param cont the parent container which will contain the new component. - * @return true if the component was created and added, else false. - */ - private Comp addCompIfPossible(TreeMapNode node, Container cont) { - Rectangle2D rect = node.getRectangle(); - // if the ndoe's rectangle is smaller than the container, it is added - if (cont.getWidth() > rect.getWidth() + X_PADDING && - cont.getHeight() > rect.getHeight() + Y_PADDING) { - - Comp toReturn = renderizeNode(node); - if (toReturn == null) { - return null; - } - addLabelIfPossible(TITLE + node.getLabel(), toReturn); - - // leaves some space from the parent's origin location - Point loc = toReturn.getLocation(); - loc.x += X_PADDING; - loc.y += Y_PADDING; - toReturn.setLocation(loc); - - cont.add(toReturn); - return toReturn; - } - return null; - } - - - /** - * This method recalculates and redraws the TreeMap in according to the size - * of this component and the actual {@link TreeMapNode} object. - */ - private void redrawTreeMap(TreeMapNode newRoot) { - tree = newRoot; - Rectangle2D.Double newArea = tree.getRectangle(); - // give to the root node the size of this object so it can be recalculated - newArea.width = getSize().width; - newArea.height = getSize().height; - - // recalculate the tree - TreeProcessor.processTreeMap(tree, newArea); - - removeAll(); - drawTreeMap(tree); - } - - boolean isZoomInEnabled(TreeMapNode node) { - return !(node == null - || node.equals(this.tree) - || node.isLeaf()); - } - - public void zoomIn(TreeMapNode node) { - if (isZoomInEnabled(node)) { - fillZoomStack(node.getAncestors()); - redrawTreeMap(node); - notifyZoomInToObservers(zoomStack.peek()); - } - } - - private void fillZoomStack(LinkedList<TreeMapNode> ancestors) { - zoomStack.clear(); - while (!ancestors.isEmpty()) { - zoomStack.push(ancestors.removeLast()); - } - } - - public void zoomOut() { - // if the actual root element is not the tree's original root - if (zoomStack.size() > 1) { - zoomStack.pop(); - redrawTreeMap(zoomStack.peek()); - notifyZoomOutToObservers(); - } - } - - /** - * Zoom out the view directly to the original root. - */ - public void zoomFull() { - if (zoomStack.size() > 1) { - clearZoomCallsStack(); - redrawTreeMap(zoomStack.peek()); - notifyZoomFullToObservers(); - } - } - - /** - * Add the object in input to the list of registered objects to this TreeMap. - * @param observer the Notifiable object to register to this object. - */ - public void register(TreeMapObserver observer) { - this.observers.add(observer); - } - - /** - * Remove the object in input from the list of registered objects to this TreeMap. - * @param observer the Notifiable object to unregister from this object. - */ - public void unregister(TreeMapObserver observer) { - this.observers.remove(observer); - } - /** - * Notify observers that an object in the TreeMap has been selected. - * @param comp the selected component. - */ - private void notifySelectionToObservers(TreeMapNode node) { - for (TreeMapObserver observer : observers) { - observer.notifySelection(node); - } - } - - /** - * Notify observers that TreeMap has been zoomed. - * @param zoomedComponent - */ - private void notifyZoomInToObservers(TreeMapNode node) { - for (TreeMapObserver observer : observers) { - observer.notifyZoomIn(node); - } - } - - /** - * Notify observers that TreeMap has been zoomed. - * @param zoomedComponent - */ - private void notifyZoomOutToObservers() { - for (TreeMapObserver observer : observers) { - observer.notifyZoomOut(); - } - } - - /** - * Notify observers that TreeMap has been zoomed. - * @param zoomedComponent - */ - private void notifyZoomFullToObservers() { - for (TreeMapObserver observer : observers) { - observer.notifyZoomFull(); - } - } - - - - /** - * Returns the list of zoom operation calls. - * @return the stack that holds the zoom calls. - */ - public Stack<TreeMapNode> getZoomCallsStack() { - return zoomStack; - } - - /** - * Clear the zoom calls of this object leaving the stack with just the root. - */ - public void clearZoomCallsStack() { - while (zoomStack.size() > 1) { - zoomStack.pop(); - } - } - - /** - * check if last resize operation was called too closer to this - * one. If so, ignore it: the container is being dragged. - * - * @return true if this method is invoked at distance of - * MIN_DRAGGING_TIME millisec, else false. - */ - private boolean canResize(int millisec) { - long time = System.currentTimeMillis(); - if (time - lastCall >= millisec) { - lastCall = time; - return true; - } - return false; - } - - - /** - * Check if the dimension given in input differs from the last one stored - * by 2. - * @param newDim the new dimension to check. - * @return true if the dimensions are different, else false. - */ - private boolean isChangedSize(Dimension newDim) { - int minResizeDim = 2; - int deltaX = Math.abs(newDim.width - lastDim.width); - int deltaY = Math.abs(newDim.height - lastDim.height); - - if (deltaX > minResizeDim || deltaY > minResizeDim) { - lastDim = newDim; - return true; - } - return false; - } - - /** - * Switch the component's visualization mode to the one given in input. - * Use static constraints to set correctly a visualization mode. - * @param constraint the UI visualization mode to set. - */ - public void setBorderStyle(int UIMode) { - this.borderStyle = UIMode; - switch (borderStyle) { - case 1 : { - defaultBorder = new EmptyBorder(0, 0, 0, 0); - break; - } - case 2 : { - defaultBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED, Color.white, Color.darkGray); - break; - } - case 3 : { - defaultBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.white, Color.darkGray); - break; - } - default : { - defaultBorder = new LineBorder(Color.black, 1); - break; - } - } - applyBorderToSubtree(mainComp); - } - - /** - * Traverse recursively the tree from the given component applying to it - * the default border. - * @param comp the subtree's root from which apply the border style. - */ - private void applyBorderToSubtree(Comp comp) { - comp.setBorder(defaultBorder); - Component[] children = comp.getComponents(); - for (int i = 0; i < children.length; i++) { - if (children[i] instanceof Comp) { - applyBorderToSubtree((Comp) children[i]); - } - } - } - - /** - * Return the last clicked component inside the TreeMap. - * @return the last clicked {@Comp} object. - */ - public Comp getClickedComponent() { - return lastClicked; - } - - /** - * This class provides an extension of {@link JLabel} which main - * characteristic is to implement the {@link Cloneable} interface in order - * to make his creation faster then JLabel class. - */ - class Label extends JLabel implements Cloneable { - private static final long serialVersionUID = 1L; - - public Label(String s) { - super(s); - setFont(FONT); - setBounds(0, 0, getPreferredSize().width, FONT_SIZE); - } - - @Override - protected JLabel clone() { - Label clone = new Label(""); - clone.setFont(getFont()); - clone.setText(getText()); - clone.setBackground(getBackground()); - clone.setBounds(getBounds()); - clone.setBorder(getBorder()); - return clone; - } - } - - /** - * This class provides an extension of {@link JComponent} which main - * characteristic is to implement {@link Cloneable} interface in order to - * make his creation faster. <br> - * It also provides some action listeners that allow to select it, performing - * zoom operations for the treemap. - */ - class Comp extends JComponent implements Cloneable { - - private static final long serialVersionUID = 1L; - - /** - * The node represented by this component. - */ - private TreeMapNode node; - - /** - * The background color. It depends by the node's depth. - */ - private Color color; - - /** - * Reference to this. - */ - private Comp thisComponent; - - public Comp() { - super(); - thisComponent = this; - addClickListener(this); - addMouseListener(this); - } - - @Override - public Comp clone() { - Comp clone = new Comp(); - clone.setBounds(getBounds()); - clone.setBorder(getBorder()); - clone.setLayout(getLayout()); - clone.setOpaque(true); - return clone; - } - - public void setNode(TreeMapNode node) { - this.node = node; - this.color = node.getColor(); - ValueFormatter f = new ValueFormatter(this.node.getRealWeight()); - this.setToolTipText(this.node.getLabel() + " - " + f.format()); - } - - public TreeMapNode getNode() { - return this.node; - } - - public Color getColor() { - return this.color; - } - - public void setColor(Color c) { - this.color = c; - } - - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - if (this.color != null) { - g.setColor(color); - g.fillRect(0, 0, getWidth(), getHeight()); - } - } - - /** - * Add a mouse listener to this component. It allows to select it and - * zoom it. - * @param component the component which will have the mouse listener. - */ - private void addClickListener(final JComponent component) { - MouseListener click = new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - // one left click select the rectangle - if (SwingUtilities.isLeftMouseButton(e)) { - selectComp(); - } - // double left click to zoom in (on non-leaf nodes only) - if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) { - zoomIn(getNode()); - } - // one right click to zoom out - if (SwingUtilities.isRightMouseButton(e)) { - zoomOut(); - } - // one middle click to reset zoom - if (SwingUtilities.isMiddleMouseButton(e)) { - zoomFull(); - } - } - }; - component.addMouseListener(click); - } - - /** - * Add a mouse motion listener to this component. This allows for the mouse cursor to be changed into a - * magnifying glass icon when the cursor enters a zoomable component, and back to a default cursor when it - * exits a zoomable component. - * @param component the component which will have the mouse motion listener. - */ - private void addMouseListener(final JComponent component) { - MouseListener listener = new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - if (getNode().isLeaf()) { - setDefaultCursor(); - } else { - setZoomableCursor(); - } - } - - @Override - public void mouseExited(MouseEvent e) { - if (!getNode().isLeaf()) { - setDefaultCursor(); - } else { - setZoomableCursor(); - } - } - - private void setZoomableCursor() { - component.setCursor(ThermostatSwingCursors.getZoomIconCursor()); - } - - private void setDefaultCursor() { - component.setCursor(Cursor.getDefaultCursor()); - } - }; - component.addMouseListener(listener); - } - - /** - * This method gives a darker color to this component and restore the - * original color to the last selected component. - */ - private void selectComp() { - if (lastClicked != null) { - if (!lastClicked.getNode().isLeaf()) { - lastClicked.setColor(lastClicked.getColor().brighter()); - } - lastClicked.repaint(); - } - lastClicked = thisComponent; - if (!getNode().isLeaf()) { - setColor(getColor().darker()); - } - repaint(); - notifySelectionToObservers(node); - } - } -} - - - - -
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNode.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,447 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.Color; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * This class provide a tree recursive implementation used in - * {@link SquarifiedTreeMap}. It contains a reference to the parent node and to - * a node list, which represent the node's children. It is also - * possible to store generic information inside the node using a {@link Map} - * object. Furthermore, the main property of this class is the chance to have a - * weight for the node and associate to it a {@link Rectangle2D.Double} object. - * - * <p>When an instance of this class is created, it will automatically be - * assigned a unique id. - * - * <p>By default, this class' comparator is based on the nodes' weight. - * - * <p>A static Quick Sort algorithm implementation is also provided by this - * class. - * - * @see Rectangle2D.Double - */ -public class TreeMapNode { - - /** - * Counter for assign unique id to nodes. - */ - private static int idCounter = 0; - - /** - * The rectangle which will graphically represent this node. - */ - private Rectangle2D.Double rectangle; - - /** - * This node's id. - */ - private int id; - - /** - * A Map in which store information for this node. - */ - private Map<String, String> info; - - /** - * Reference to the parent. - */ - private TreeMapNode parent; - - /** - * Reference to children. - */ - private List<TreeMapNode> children; - - /** - * The node's weight. - */ - private double weight; - - /** - * The node's label. It can be the same of another node. - */ - private String label; - - /** - * The node's weight which has been set inside the constructor. All - * operations which refers to node's weight work on the weight field, that - * is used to make calcs. - */ - private double realWeight; - - /** - * This flag indicates if weight value can be a non positive number. - */ - static boolean allowNonPositiveWeight = false; - - /** - * The color of this node. - */ - private Color color; - - /** - * Colors available on which iterate - */ - static final Color[] colors = { - Color.decode("#FACED2"), // red - Color.decode("#B9D6FF"), // blue - Color.decode("#E5E5E5"), // grey - Color.decode("#FFE7C7"), // orange - Color.decode("#ABEBEE"), // aqua - Color.decode("#E4D1FC"), // purple - Color.decode("#FFFFFF"), // white - Color.decode("#CDF9D4") // green - }; - - public final Color START_COLOR = colors[0]; - - /** - * - * Constructor that allow to set the nodes' real weight. Others fields are - * initialized to their default value. - * It automatically set the node's id. - * - * <p> - * @param realWeight the nodes real weight, which will be not affected - * during node processing. - * - */ - public TreeMapNode(double realWeight) { - this("", realWeight); - } - - /** - * - * Constructor that allow to set the nodes' real weight and the label. - * Others fields are initialized to their default value. - * It automatically set the node's id. - * - * <p> - * @param label the node's label. - * @param realWeight the nodes real weight, which will be not affected - * during node processing. - * - */ - public TreeMapNode(String label, double realWeight) { - this.id = idCounter++; - this.label = label; - this.parent = null; - this.children = new ArrayList<TreeMapNode>(); - this.rectangle = new Rectangle2D.Double(); - this.info = new HashMap<String, String>(); - this.weight = realWeight; - this.realWeight = realWeight; - } - - /** - * Return the id of this object. - * @return the id automatically assigned at this object initialization. - */ - public int getId() { - return this.id; - } - - /** - * Set this node's label. - * @param newLabel the new label to set. - */ - public void setLabel(String newLabel) { - this.label = newLabel; - } - - /** - * Return the label of this object. - * @return the label assigned at instantiation time to this object. - */ - public String getLabel() { - return this.label; - } - - /** - * Return the reference to the node parent of this object. - * @return the parent of this node. It can be null. - */ - public TreeMapNode getParent() { - return this.parent; - } - - /** - * Set as parent of this object the node given in input. - * @param parent the new parent of this object. No checks are made for null - * value. - */ - public void setParent(TreeMapNode parent) { - this.parent = parent; - } - - /** - * Return the list of nodes representing this node's children. - * @return a list of {@link TreeMapNode} objects. - */ - public List<TreeMapNode> getChildren() { - return this.children; - } - - public boolean isLeaf() { - return getChildren().isEmpty(); - } - - /** - * Set as children list of this object the list given in input. - * @param children the new list of children for this node. - */ - public void setChildren(List<TreeMapNode> children) { - this.children = children; - for (TreeMapNode child : this.children) { - child.setParent(this); - } - } - - /** - * Return the {@link Map} object containing all information of this node. - * @return a {@link Map} object. - */ - public Map<String, String> getInfo() { - return this.info; - } - - /** - * Store the given information into this object. - * @param key the key searching value for the information to store. - * @param value the information to store into this object. - * @return the old value for the given key. - */ - public String addInfo(String key, String value) { - return this.info.put(key, value); - } - - /** - * Return the information stored in this object, corresponding to the key - * given in input. - * @param key the key value for the search information. - * @return the corresponding value for the given key. - */ - public String getInfo(String key) { - return this.info.get(key); - } - - /** - * Add the object given in input to the children list of this object. It - * also add this object as its parent. - * @param child the new child to add at this object. - */ - public void addChild(TreeMapNode child) { - if (child != null) { - this.children.add(child); - child.setParent(this); - } - } - - @Override - /** - * Return a {@link String} representing this object. - */ - public String toString() { - return getClass().getSimpleName() + " [" + "label = " + getLabel() + - "; weight =" + getRealWeight() + - "; rectangle=" + rectangle.getBounds() + "]"; - } - - /** - * Return the weight of this object. In case of allowNonPositiveWeight is - * set to false and the weight is 0, less than 0 or not a number - * ({@link Double.Nan}), this method returns a value that can be transformed - * by external objects, so if you need the real weight you have to - * invoke getrealWeight(). - * - * @return the node's weight. - */ - public double getWeight() { - if ((weight <= 0 || weight == Double.NaN) && !allowNonPositiveWeight) { - return realWeight; - } - return this.weight; - } - - /** - * Use this method to retrieve the real weight assigned to this node. - * @return the weight corresponding to this node. - */ - public double getRealWeight() { - return this.realWeight; - } - - - /** - * Use this method to set the real weight of this node. - */ - public void setRealWeight(double w) { - this.realWeight = w; - } - - /** - * Set the weight of this object. If a negative value is given, it is set - * automatically to 0. - * @param weight the new weight for this object. - */ - public void setWeight(double w) { - this.weight = w < 0 && !allowNonPositiveWeight ? 0 : w; - } - - - /** - * Return the rectangle representing this object. - * @return a {@link Rectangle2D.Double} object. - */ - public Rectangle2D.Double getRectangle() { - if (this.rectangle == null) { - throw new RuntimeException(); - } - return this.rectangle; - } - - /** - * Set a new rectangle for this object. - * @param rectangle the new rectangle that represent this node. - */ - public void setRectangle(Rectangle2D.Double rectangle) { - this.rectangle = rectangle; - } - - /** - * - * @return true if non positive value can be used as weight, else false. - */ - public static boolean isAllowNonPositiveWeight() { - return allowNonPositiveWeight; - } - - /** - * Set this value to false and nodes will be not able to manage non positive - * values for weight field, otherwise set to true. - * @param allowed the flag value for managing non positive values as weight - */ - public static void setAllowNonPositiveWeight(boolean allowed) { - allowNonPositiveWeight = allowed; - } - - /** - * This method assess if the rectangle associated to this node is drawable, - * which means that its sides are greater than 1. - * @return true if the rectangle associated to this node is drawable, - * else false. - */ - public boolean isDrawable() { - if (rectangle.width >= 1 && rectangle.height >= 1) { - return true; - } - return false; - } - - public Color getColor() { - return color; - } - - public void setColor(Color color) { - this.color = color; - } - - /** - * Returns this node's next color. - * @return the color which came after this node's color in the color list. - * If this node has no color assigned then the START_COLOR is returned. - */ - public Color getNextColor() { - if (this.color != null) { - for (int i = 0; i < colors.length; i++) { - if (this.color.equals(colors[i])) { - return colors[(i + 1) % colors.length]; - } - } - } - return START_COLOR; - } - - - public int getDepth() { - if (this.parent == null) { - return 0; - } else { - return 1 + parent.getDepth(); - } - } - - /** - * This method sorts the given list in <b>descending<b> way. - * - * @param nodes the list of {@link TreeMapNode} to sort. - */ - public static void sort(List<TreeMapNode> nodes) { - Comparator<TreeMapNode> c = new Comparator<TreeMapNode>() { - @Override - public int compare(TreeMapNode o1, TreeMapNode o2) { - // inverting the result to descending sort the list - return -(Double.compare(o1.getWeight(), o2.getWeight())); - } - }; - Collections.sort(nodes, c); - } - - /** - * Return the list of ancestors node of this object. The first one is this - * node itself, the last one the root. - * @return a list of ancestors nodes. - */ - public LinkedList<TreeMapNode> getAncestors() { - LinkedList<TreeMapNode> toReturn = new LinkedList<TreeMapNode>(); - TreeMapNode tmp = this; - do { - toReturn.add(tmp); - } while ((tmp = tmp.getParent()) != null); - return toReturn; - } -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapObserver.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -/** - * This interface is used as part of the Observer Design Pattern developed - * for objects who want to be notified about TreeMap's events. - */ -public interface TreeMapObserver { - - /** - * This method inform the Observer object that the object passed as - * argument has been selected. - * - * @param selectedComp the selected component to communicate to - * this Observer object. - */ - public void notifySelection(TreeMapNode node); - - /** - * This method informs objects that a zoom in event has been performed on - * the given node. - * @param node the zoomed node. - */ - public void notifyZoomIn(TreeMapNode node); - - /** - * This method informs objects that a zoom out event has been performed. - */ - public void notifyZoomOut(); - - /** - * This method informs objects that the zoom level has been resetted. - */ - public void notifyZoomFull(); -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapPanel.java Wed Jun 01 15:58:04 2016 -0400 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapPanel.java Mon Jun 06 15:17:50 2016 -0400 @@ -38,10 +38,13 @@ import java.awt.BorderLayout; import java.awt.Component; +import java.awt.Dimension; import javax.swing.JPanel; import com.redhat.thermostat.client.swing.SwingComponent; +import com.redhat.thermostat.client.swing.components.experimental.TreeMapComponent; +import com.redhat.thermostat.client.swing.components.experimental.TreeMapToolbar; import com.redhat.thermostat.vm.heap.analysis.client.core.HeapTreeMapView; import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; @@ -58,7 +61,7 @@ @Override public void display(ObjectHistogram histogram) { - treeMap = new TreeMapComponent(histogram); + treeMap = new TreeMapComponent(HistogramConverter.convertToTreeMap(histogram), new Dimension()); panel.add(treeMap, BorderLayout.CENTER); panel.add(new TreeMapToolbar(treeMap), BorderLayout.NORTH); }
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapToolbar.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.AdjustmentEvent; -import java.awt.event.AdjustmentListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.util.Objects; - -import javax.swing.Box; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.ScrollPaneConstants; - -/** - * This class provides a tool bar containing a {@lnk TreeMapBreadcrumb} object - * and a {@link TreeMapZoomBar} instance to control the state of - * {@link TreeMapComponent} objects. - */ -@SuppressWarnings("serial") -public class TreeMapToolbar extends JComponent { - - /** - * The panel in which objects are placed. - */ - private JPanel contentPane; - - /** - * The scroll pane used to hide breadcrumb's first items. - */ - private JScrollPane scrollPane; - - - public TreeMapToolbar(TreeMapComponent treemap) { - super(); - initComponent(Objects.requireNonNull(treemap)); - } - - - private void initComponent(TreeMapComponent treemap) { - this.setLayout(new BorderLayout()); - - final JPanel breadcrumbPanel = new JPanel(); - breadcrumbPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); - add(breadcrumbPanel, BorderLayout.CENTER); - - JPanel zoomPanel = new JPanel(new FlowLayout()); - // add some empty space between the breadcrumb bar and buttons - zoomPanel.add(Box.createHorizontalStrut(20)); - add(zoomPanel, BorderLayout.EAST); - - TreeMapZoomBar zoomBar = new TreeMapZoomBar(treemap); - zoomPanel.add(zoomBar); - - TreeMapBreadcrumb bc = new TreeMapBreadcrumb(treemap, treemap.getTreeMapRoot()); - - contentPane = new JPanel(new FlowLayout(FlowLayout.LEFT)); - contentPane.add(bc); - - scrollPane = new JScrollPane(contentPane); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); - scrollPane.setBorder(null); - - // allows to see always the last elements of the breadcrumb. - scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() { - public void adjustmentValueChanged(AdjustmentEvent e) { - e.getAdjustable().setValue(e.getAdjustable().getMaximum()); - } - }); - breadcrumbPanel.add(scrollPane); - - // when the component is resized the new dimension is used to arrange - // the scrollpane, in order to use all available space. - breadcrumbPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent arg0) { - Dimension d = breadcrumbPanel.getSize(); - d.height = 20; - scrollPane.setPreferredSize(d); - } - }); - } - -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapZoomBar.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.Color; -import java.awt.FlowLayout; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.Objects; - -import javax.swing.JComponent; -import javax.swing.JLabel; - -import com.redhat.thermostat.client.swing.components.FontAwesomeIcon; -import com.redhat.thermostat.client.swing.components.Icon; -import com.redhat.thermostat.client.ui.Palette; -import com.redhat.thermostat.shared.locale.Translate; -import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources; - -/** - * This class provides a component containing zoom in/out/full buttons which can - * control a {@link TreeMapComponent} object. - */ -public class TreeMapZoomBar extends JComponent implements TreeMapObserver { - - private static final long serialVersionUID = 1L; - - private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); - - private JLabel zoomOut; - private JLabel zoomFull; - private JLabel zoomIn; - - private Color defaultColor = Palette.BLACK.getColor(); - private Color enterColor = Palette.THERMOSTAT_BLU.getColor(); - - /** - * The treemap object to interact with. - */ - private TreeMapComponent treemap; - - /** - * If an item is selected in the treemap, it is stored in order to zoom on - * it if the ZoomIn button is pressed. - */ - private TreeMapNode selectedItem; - - /** - * Constructor. It creates the zoom buttons and registers this object as - * treemap observer. - */ - public TreeMapZoomBar(TreeMapComponent treemap) { - super(); - this.treemap = Objects.requireNonNull(treemap); - initComponent(); - treemap.register(this); - } - - private void initComponent() { - this.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); - createZoomInButton(); - createZoomFullButton(); - createZoomOutButton(); - - /* - * At the beginning no actions can be performed: - * Cannot zoom in because no item is selected; - * cannot zoom out because zoom in hasn't been performed; - * the same for zoom full. - */ - zoomIn.setEnabled(false); - zoomFull.setEnabled(false); - zoomOut.setEnabled(false); - } - - - - private void createZoomInButton() { - zoomIn = new JLabel(); - final Icon baseIcon = new FontAwesomeIcon('\uf065', 15, defaultColor); - final Icon hoverIcon = new FontAwesomeIcon('\uf065', 15, enterColor); - - zoomIn.setIcon(baseIcon); - zoomIn.setToolTipText(t.localize(LocaleResources.ZOOM_IN).getContents()); - - zoomIn.addMouseListener(new MouseAdapter() { - @Override - public void mouseExited(MouseEvent e) { - zoomIn.setIcon(baseIcon); - } - - @Override - public void mouseEntered(MouseEvent e) { - zoomIn.setIcon(hoverIcon); - } - - @Override - public void mouseClicked(MouseEvent arg0) { - if (selectedItem != null) { - treemap.zoomIn(selectedItem); - } - } - }); - - this.add(zoomIn); - } - - private void createZoomOutButton() { - zoomOut = new JLabel(); - - final Icon baseIcon = new FontAwesomeIcon('\uf066', 15, defaultColor); - final Icon hoverIcon = new FontAwesomeIcon('\uf066', 15, enterColor); - - zoomOut.setIcon(baseIcon); - zoomOut.setToolTipText(t.localize(LocaleResources.ZOOM_OUT).getContents()); - zoomOut.addMouseListener(new MouseAdapter() { - @Override - public void mouseExited(MouseEvent e) { - zoomOut.setIcon(baseIcon); - } - - @Override - public void mouseEntered(MouseEvent e) { - zoomOut.setIcon(hoverIcon); - } - - @Override - public void mouseClicked(MouseEvent arg0) { - treemap.zoomOut(); - } - }); - - this.add(zoomOut); - } - - private void createZoomFullButton() { - zoomFull = new JLabel(); - - final Icon baseIcon = new FontAwesomeIcon('\uf03b', 15, defaultColor); - final Icon hoverIcon = new FontAwesomeIcon('\uf03b', 15, enterColor); - - zoomFull.setIcon(baseIcon); - zoomFull.setToolTipText(t.localize(LocaleResources.ZOOM_FULL).getContents()); - zoomFull.addMouseListener(new MouseAdapter() { - @Override - public void mouseExited(MouseEvent e) { - zoomFull.setIcon(baseIcon); - } - - @Override - public void mouseEntered(MouseEvent e) { - zoomFull.setIcon(hoverIcon); - } - - @Override - public void mouseClicked(MouseEvent arg0) { - treemap.zoomFull(); - } - }); - - this.add(zoomFull); - } - - /** - * Changes the buttons state in according to the treemap view. - */ - private void changeState() { - selectedItem = null; - zoomIn.setEnabled(false); - - if (!isRootShown()) { - zoomFull.setEnabled(true); - zoomOut.setEnabled(true); - } else { - zoomFull.setEnabled(false); - zoomOut.setEnabled(false); - } - } - - @Override - public void notifySelection(TreeMapNode node) { - selectedItem = node; - zoomIn.setEnabled(treemap.isZoomInEnabled(node)); - } - - @Override - public void notifyZoomIn(TreeMapNode node) { - changeState(); - } - - @Override - public void notifyZoomOut() { - changeState(); - } - - @Override - public void notifyZoomFull() { - // no actions can be performed - zoomFull.setEnabled(false); - zoomOut.setEnabled(false); - zoomIn.setEnabled(false); - } - - private boolean isRootShown() { - return treemap.getTreeMapRoot() == treemap.getZoomCallsStack().firstElement(); - } - -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeProcessor.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.Color; -import java.awt.geom.Rectangle2D; -import java.util.Objects; - - -public class TreeProcessor { - - /** - * Padding between the main component and its sub component. - */ - public static final int X_PADDING = 15; - public static final int Y_PADDING = 20; - - /** - * This method process recursively the tree nested in the node element - * passed as argument in the constructor calculating the children TreeMap - * for each node also applying coloring. - * @return the updated tree, where nodes have additional information like - * {@link Rectangle2D>Float} instance and a color. - */ - public static TreeMapNode processTreeMap(TreeMapNode tree, Rectangle2D.Double area) { - Objects.requireNonNull(tree); - Objects.requireNonNull(area); - tree.setRectangle(area); - if (tree.getColor() == null) { - tree.setColor(tree.START_COLOR); - } - - process(tree); - return tree; - } - - /** - * This method is used to effectively process the whole tree structure. It - * uses a {@link SquarifiedTreeMap} object to calculate a TreeMap for each - * node who has children. - * @param node the subtree's root to process - */ - private static void process(TreeMapNode node) { - - SquarifiedTreeMap algorithm = new SquarifiedTreeMap(getSubArea(node.getRectangle()), node.getChildren()); - node.setChildren(algorithm.squarify()); - - Color c = node.getNextColor(); - - for (TreeMapNode child : node.getChildren()) { - //children will have all the same color, which is the parent's next one - if (child.getColor() == null) { - child.setColor(c); - } - // if squarified rectangles have drawable sides then continue to - // process, else don't process the subtree having as root a - // non drawable rectangle. - if (child.isDrawable()) { - process(child); - } - } - } - - /** - * Calculate space and coordinates in which children's rectangle will be - * drawn, from the main component. - * @return the rectangle representing the new available area. - */ - private static Rectangle2D.Double getSubArea(Rectangle2D.Double area) { - Rectangle2D.Double subArea = new Rectangle2D.Double(); - subArea.setRect(area); - - subArea.width = Math.max(0, (subArea.width - 2 * X_PADDING)); - subArea.height = Math.max(0, (subArea.height - 1.5 * Y_PADDING)); - return subArea; - } -}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ValueFormatter.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -public class ValueFormatter { - - private double value; - - public ValueFormatter(double val) { - this.value = val; - } - - /** - * This method returns the node value calculating it in bytes, KB or MB. - * - * i.e. if node's weight = 200 it returns: "200 bytes" <br> - * if weight = 20152: "20.15 KB" <br> - * if weight = 2015248: "2.01 MB" <br> - * - * Note that float values are approximated to the second decimal digit. - */ - public String format() { - int KB = 1000; - int MB = 1000000; - String unit = "Bytes"; - - if (value >= KB && value < MB) { - value /= KB; - unit = "KBytes"; - } else if (value >= MB) { - value /= MB; - unit = "MBytes"; - } - // show 2 decimal digits - String formattedValue = String.format("%.2f", value); - return formattedValue + " " + unit; - } -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HistogramConverterTest.java Wed Jun 01 15:58:04 2016 -0400 +++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HistogramConverterTest.java Mon Jun 06 15:17:50 2016 -0400 @@ -45,6 +45,7 @@ import org.junit.Before; import org.junit.Test; +import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; import com.sun.tools.hat.internal.model.JavaClass; import com.sun.tools.hat.internal.model.JavaHeapObject;
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SquarifiedTreeMapTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -/** - * Using eclEmma tool has been proved that this test covers 100% - * of {@link SquarifiedTreeMap} code and also 90% of {@link TreeMapBuilder} code. - */ -public class SquarifiedTreeMapTest { - - private SquarifiedTreeMap algorithm; - Rectangle2D.Double bounds; - List<TreeMapNode> list; - - @Before - public void setUp() throws Exception { - bounds = new Rectangle2D.Double(0, 0, 10, 5); - list = new ArrayList<>(); - } - - @Test - public final void testSquarifiedTreeMap() { - //check every parameters combinations - boolean catched = false; - try { - algorithm = new SquarifiedTreeMap(null, null); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - catched = false; - - try { - algorithm = new SquarifiedTreeMap(bounds, null); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - catched = false; - - try { - algorithm = new SquarifiedTreeMap(null, list); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - } - - @Test - public final void testSquarify() { - // test using an empty node list - algorithm = new SquarifiedTreeMap(bounds, new ArrayList<TreeMapNode>()); - assertEquals(0, algorithm.squarify().size()); - - // test using a correct list - int n = 10; - for (int i = 0; i < n; i++) { - list.add(new TreeMapNode(i+1)); - } - // process the list - algorithm = new SquarifiedTreeMap(bounds, list); - list = algorithm.squarify(); - - assertEquals(n, list.size()); - - for (int i = 0; i < n; i++) { - // node has been processed - assertNotNull(list.get(i).getRectangle()); - } - - assertEquals(list, algorithm.getSquarifiedNodes()); - } -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapComponentTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,293 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.awt.Dimension; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.SwingUtilities; - -import junit.framework.Assert; - -import org.junit.BeforeClass; -import org.junit.Test; - - -public class TreeMapComponentTest { - - private TreeMapComponent treeMap; - private static TreeMapNode tree; - private static TreeMapNode node1; - private static TreeMapNode node2; - private static Dimension dim; - - @BeforeClass - public static void setUpOnce() { - tree = new TreeMapNode(1); - node1 = new TreeMapNode(1); - node2 = new TreeMapNode(1); - tree.addChild(node1); - node1.addChild(node2); - dim = new Dimension(500, 500); - } - - - @Test - public final void testTreeMapComponent() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - - boolean catched = false; - - try { - treeMap = new TreeMapComponent(tree, dim); - // pass - } catch(NullPointerException e) { - Assert.fail("Didn't expect exception."); - } - try { - treeMap = new TreeMapComponent(null, null); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - catched = false; - - try { - treeMap = new TreeMapComponent(tree, null); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - catched = false; - - try { - treeMap = new TreeMapComponent(null, dim); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - } - }); - } - - @Test - public final void testGetRoot() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - treeMap = new TreeMapComponent(tree, dim); - assertEquals(tree, treeMap.getTreeMapRoot()); - } - }); - } - - @Test - public final void testIsZoomInEnabled() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - TreeMapComponent treeMap = new TreeMapComponent(tree, dim); - - assertFalse("Should not be able to zoom in on null", treeMap.isZoomInEnabled(null)); - assertFalse("Should not be able to zoom in on root", treeMap.isZoomInEnabled(tree)); - assertTrue("Should be able to zoom in on node 1", treeMap.isZoomInEnabled(node1)); - assertFalse("Should not be able to zoom in on node 2", treeMap.isZoomInEnabled(node2)); - } - }); - } - - @Test - public final void testZoomIn() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - TreeMapComponent treeMap = new TreeMapComponent(tree, dim); - - treeMap.zoomIn(node1); - assertEquals(node1, treeMap.getTreeMapRoot()); - - treeMap.zoomIn(node2); - assertEquals(node1, treeMap.getTreeMapRoot()); - } - }); - } - - @Test - public final void testZoomOut() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - treeMap = new TreeMapComponent(tree, dim); - - treeMap.zoomOut(); - assertEquals(tree, treeMap.getTreeMapRoot()); - - treeMap.zoomIn(node1); //if zoom out root is tree - treeMap.zoomIn(node2); //no-op, cannot zoom on leaf - - assertEquals(node1, treeMap.getTreeMapRoot()); - - treeMap.zoomOut(); - assertEquals(tree, treeMap.getTreeMapRoot()); - } - }); - } - - @Test - public final void testZoomFull() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - treeMap = new TreeMapComponent(tree, dim); - - treeMap.zoomIn(node2); - treeMap.zoomFull(); - assertEquals(tree, treeMap.getTreeMapRoot()); - - } - }); - } - - @Test - public final void testGetZoomCallsStack() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - treeMap = new TreeMapComponent(tree, dim); - - // the root is always in the stack - assertEquals(1, treeMap.getZoomCallsStack().size()); - - treeMap.zoomIn(tree); - // zooming on the same element nothing happen - assertEquals(1, treeMap.getZoomCallsStack().size()); - - treeMap.zoomIn(node1); - treeMap.zoomIn(node2); - treeMap.zoomFull(); - assertEquals(tree, treeMap.getTreeMapRoot()); - } - }); - } - - - @Test - public final void testClearZoomCallsStack() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - treeMap = new TreeMapComponent(tree, dim); - - treeMap.clearZoomCallsStack(); - assertEquals(1, treeMap.getZoomCallsStack().size()); - - treeMap.zoomIn(node1); - treeMap.zoomIn(node2); - treeMap.clearZoomCallsStack(); - assertEquals(1, treeMap.getZoomCallsStack().size()); - } - }); - } - - @Test - public final void testObserver() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - boolean zoomedIn = false; - boolean zoomedOut = false; - boolean zoomedFull = false; - - TreeMapObserver observer = new TreeMapObserver() { - @Override - public void notifyZoomOut() { - zoomedOut = true; - } - - @Override - public void notifyZoomIn(TreeMapNode node) { - zoomedIn = true; - } - - @Override - public void notifyZoomFull() { - zoomedFull = true; - } - - @Override - public void notifySelection(TreeMapNode node) { - } - }; - - @Override - public void run() { - TreeMapNode child = new TreeMapNode(1); - tree.addChild(child); - TreeMapNode grandchild = new TreeMapNode(1); - child.addChild(grandchild); - - treeMap = new TreeMapComponent(tree, dim); - treeMap.register(observer); - - treeMap.zoomIn(child); - assertTrue("Should have zoomed in on child", zoomedIn); - zoomedIn = false; - - treeMap.zoomIn(grandchild); - assertFalse("Should not have zoomed in on grandchild", zoomedIn); - - treeMap.zoomOut(); - assertTrue("Should have zoomed out", zoomedOut); - - treeMap.zoomIn(child); - treeMap.zoomFull(); - assertTrue("Should have zoomed full", zoomedFull); - } - }); - } -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNodeTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.awt.Color; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - - -public class TreeMapNodeTest { - - private TreeMapNode node; - private static final double DELTA = 0.001; - - @Before - public void setUp() { - node = new TreeMapNode(null, 1); - } - - @Test - public final void testGetId() { - TreeMapNode node1 = new TreeMapNode(null, 1); - TreeMapNode node2 = new TreeMapNode(null, 1); - assertTrue(node1.getId() != node2.getId()); - assertTrue(node1.getId() + 1 == node2.getId()); - } - - @Test - public final void testGetSetParent() { - TreeMapNode parent = new TreeMapNode(null, 1); - assertTrue(node.getParent() == null); - node.setParent(parent); - assertTrue(node.getParent() == parent); - } - - @Test - public final void testGetSetLabel() { - TreeMapNode node = new TreeMapNode("MyLabel", 1); - assertTrue(node.getLabel().equals("MyLabel")); - node.setLabel("MyNewLabel"); - assertTrue(node.getLabel().equals("MyNewLabel")); - } - - @Test - public final void testGetSetChildren() { - assertTrue(node.getChildren().isEmpty()); - - TreeMapNode node = new TreeMapNode(null, 1); - List<TreeMapNode> children = new ArrayList<>(); - children.add(node); - - node.setChildren(children); - assertTrue(1 == node.getChildren().size()); - } - - @Test - public final void testIsLeaf() { - assertTrue(node.isLeaf()); - node.addChild(new TreeMapNode(null, 1)); - assertFalse(node.isLeaf()); - node.setChildren(Collections.<TreeMapNode>emptyList()); - assertTrue(node.isLeaf()); - } - - @Test - public final void testGetAddInfo() { - node.addInfo("exampleKey", "exampleValue"); - assertEquals("exampleValue", node.getInfo("exampleKey")); - } - - @Test - public final void testAddChild() { - assertTrue(node.getChildren().size() == 0); - node.addChild(new TreeMapNode(null, 1)); - assertTrue(node.getChildren().size() == 1); - - node.addChild(null); - assertTrue(node.getChildren().size() == 1); // null has not been added - } - - @Test - public final void testGetSetWeight() { - assertEquals(1.0, node.getWeight(), DELTA); - node.setWeight(5); - assertEquals(5.0, node.getWeight(), DELTA); - } - - @Test - public final void testGetSetRectangle() { - Rectangle2D.Double r = new Rectangle2D.Double(5, 5, 5, 5); - node.setRectangle(r); - assertEquals(r, node.getRectangle()); - - node.setRectangle(null); - boolean catched = false; - try { - node.getRectangle(); - } catch(RuntimeException e) { - catched = true; - } - assertTrue(catched); - } - - - @Test - public final void testGetSetRealWeight() { - node = new TreeMapNode(null, 5); - assertEquals(5.0, node.getRealWeight(), DELTA); - node.setRealWeight(8); - assertEquals(8.0, node.getRealWeight(), DELTA); - } - - @Test - public final void testAllowNonPositiveWeight() { - assertFalse(TreeMapNode.isAllowNonPositiveWeight()); - node.setWeight(-5); - assertEquals(1.0, node.getWeight(), DELTA); - assertEquals(1.0, node.getRealWeight(), DELTA); - - TreeMapNode.setAllowNonPositiveWeight(true); - node.setWeight(-5); - assertEquals(-5.0, node.getWeight(), DELTA); - } - - @Test - public final void testIsDrawable() { - Rectangle2D.Double r = new Rectangle2D.Double(5, 5, 5, 5); - node.setRectangle(r); - assertTrue(node.isDrawable()); - - r.setRect(0, 0, 0.5f, 0.5f); - assertFalse(node.isDrawable()); - - r.setRect(0, 0, 5f, 0.5f); - assertFalse(node.isDrawable()); - - r.setRect(0, 0, 0.5f, 5f); - assertFalse(node.isDrawable()); - } - - @Test - public final void testGetSetColor() { - assertNull(node.getColor()); - node.setColor(Color.black); - assertEquals(Color.black, node.getColor()); - } - - @Test - public final void testGetDepth() { - TreeMapNode depth1 = new TreeMapNode(null, 1); - TreeMapNode depth2 = new TreeMapNode(null, 1); - - node.addChild(depth1); - depth1.addChild(depth2); - - assertTrue(node.getDepth() == 0); - assertTrue(depth1.getDepth() == 1); - assertTrue(depth2.getDepth() == 2); - } - - @Test - public final void testSort() { - - TreeMapNode n1 = new TreeMapNode(null, 5); - TreeMapNode n2 = new TreeMapNode(null, 4); - TreeMapNode n4 = new TreeMapNode(null, 2); - TreeMapNode n3 = new TreeMapNode(null, 3); - TreeMapNode n5 = new TreeMapNode(null, 0); - TreeMapNode n6 = new TreeMapNode(null, 7); - TreeMapNode n7 = new TreeMapNode(null, 1); - TreeMapNode n8 = new TreeMapNode(null, 9); - - List<TreeMapNode> toSort = new ArrayList<>(); - toSort.add(n3); - toSort.add(n2); - toSort.add(n4); - toSort.add(n1); - toSort.add(n5); - toSort.add(n6); - toSort.add(n7); - toSort.add(n8); - - TreeMapNode.sort(toSort); - - assertEquals(toSort.get(0), n8); - assertEquals(toSort.get(1), n6); - assertEquals(toSort.get(2), n1); - assertEquals(toSort.get(3), n2); - assertEquals(toSort.get(4), n3); - assertEquals(toSort.get(5), n4); - assertEquals(toSort.get(6), n7); - assertEquals(toSort.get(7), n5); - } - - @Test - public final void testGetInfo() { - Map<String, String> map = node.getInfo(); - assertNotNull(map); - assertEquals(0, map.keySet().size()); - } - - @Test - public final void testToString() { - assertNotNull(node.toString()); - } - - - @Test - public final void testGetNextColor() { - assertNull(node.getColor()); - assertTrue(node.getNextColor().equals(node.START_COLOR)); - - Color start = node.START_COLOR; - node.setColor(start); - - for (int i = 0; i < TreeMapNode.colors.length; i++) { - assertEquals(TreeMapNode.colors[i], node.getColor()); - node.setColor(node.getNextColor()); - } - } - - - @Test - public final void testGetAncestors() { - TreeMapNode node1 = new TreeMapNode(0); - TreeMapNode node2 = new TreeMapNode(0); - TreeMapNode node3 = new TreeMapNode(0); - TreeMapNode node4 = new TreeMapNode(0); - - node1.addChild(node2); - node2.addChild(node3); - node3.addChild(node4); - - LinkedList<TreeMapNode> ancestors = node4.getAncestors(); - - assertEquals(node1, ancestors.get(3)); - assertEquals(node2, ancestors.get(2)); - assertEquals(node3, ancestors.get(1)); - assertEquals(node4, ancestors.get(0)); - } - -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapToolbarTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertTrue; - -import java.awt.Dimension; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.SwingUtilities; - -import junit.framework.Assert; - -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import com.redhat.thermostat.annotations.internal.CacioTest; - -@Category(CacioTest.class) -public class TreeMapToolbarTest { - - private TreeMapComponent treeMap; - @SuppressWarnings("unused") - private TreeMapToolbar toolbar; - - private static TreeMapNode tree; - private static Dimension dim; - - @Test - public final void testTreeMapToolbar() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - - tree = new TreeMapNode(1); - dim = new Dimension(500, 500); - treeMap = new TreeMapComponent(tree, dim); - - boolean catched = false; - try { - toolbar = new TreeMapToolbar(null); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - try { - toolbar = new TreeMapToolbar(treeMap); - } catch (NullPointerException e) { - Assert.fail("Should not throw any exception."); - } - } - }); - } - - -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapZoomBarTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertTrue; - -import java.awt.Dimension; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.SwingUtilities; - -import junit.framework.Assert; - -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import com.redhat.thermostat.annotations.internal.CacioTest; - -@Category(CacioTest.class) -public class TreeMapZoomBarTest { - - private TreeMapComponent treeMap; - @SuppressWarnings("unused") - private TreeMapZoomBar zoomBar; - - private static TreeMapNode tree; - private static Dimension dim; - - @Before - public void setUp() { - tree = new TreeMapNode(1); - dim = new Dimension(500, 500); - } - - @Test - public final void testTreeMapZoomBar() throws InvocationTargetException, InterruptedException { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - boolean catched = false; - try { - zoomBar = new TreeMapZoomBar(null); - } catch(NullPointerException e) { - catched = true; - } - assertTrue(catched); - try { - treeMap = new TreeMapComponent(tree, dim); - zoomBar = new TreeMapZoomBar(treeMap); - } catch (NullPointerException e) { - Assert.fail("Should not throw any exception."); - } - } - }); - } - - -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeProcessorTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.awt.geom.Rectangle2D; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -public class TreeProcessorTest { - - private static final double DELTA = 0.1; - private static final double BASE = 2.0; - - TreeMapNode node; - Rectangle2D.Double area; - - @Before - public void setUp() throws Exception { - node = new TreeMapNode(1); - area = new Rectangle2D.Double(0, 0, 500, 500); - } - - @Test - public final void testTreeProcessor() { - boolean caught = false; - // this test check all wrong combinations for constructor parameters - try { - TreeProcessor.processTreeMap(null, area); - } catch(NullPointerException e) { - caught = true; - } - assertTrue(caught); - caught = false; - - try { - TreeProcessor.processTreeMap(node, null); - } catch(NullPointerException e) { - caught = true; - } - assertTrue(caught); - caught = false; - - try { - TreeProcessor.processTreeMap(null, null); - } catch(NullPointerException e) { - caught = true; - } - assertTrue(caught); - } - - - @Test - public final void testProcessTreeMapProcessesWholeTree() { - generateTree(node, 5, 5); - TreeProcessor.processTreeMap(node, area); - - // the test will check if any drawable node in the tree has a rectangle and a - // color, which means the processor function has processed the whole tree - traverse(node); - } - - private void traverse(TreeMapNode tree) { - if (tree.isDrawable() && (tree.getRectangle() == null || tree.getColor() == null)) { - fail("node " + tree.getId() + " not processed"); - } - for (TreeMapNode child : tree.getChildren()) { - traverse(child); - } - } - - private void generateTree(TreeMapNode root, int levels, int childrenNumber) { - if (levels == 0) { - return; - } else { - for (int i = 0; i < childrenNumber; i++) { - root.addChild(new TreeMapNode(100)); - } - for (TreeMapNode child : root.getChildren()) { - generateTree(child, levels-1, childrenNumber); - } - } - } - - @Test - public final void testProcessTreeMapNodeSizing() { - final int numSiblings = 5; - final double originalDimension = 64.0; - final double smallerDimension = 32.0; - - generateSiblingTree(node, numSiblings); - - TreeProcessor.processTreeMap(node, new Rectangle2D.Double(0, 0, originalDimension, originalDimension)); - checkNodeWeightRatios(numSiblings); - - //now resize smaller - TreeProcessor.processTreeMap(node, new Rectangle2D.Double(0, 0, smallerDimension, smallerDimension)); - - //now resize back to original size - TreeProcessor.processTreeMap(node, new Rectangle2D.Double(0, 0, originalDimension, originalDimension)); - //if the first call to checkNodeWeightRatios(numSiblings) worked, this should too - checkNodeWeightRatios(numSiblings); - } - - private void generateSiblingTree(TreeMapNode root, int numSiblings) { - assertTrue(numSiblings > 1); - root.addChild(new TreeMapNode(1.0)); //this is not a random weight - for(int i = 0; i < (numSiblings - 1); i++) { - root.addChild(new TreeMapNode(Math.pow(BASE, i))); - } - } - - private void checkNodeWeightRatios(int numSiblings) { - List<TreeMapNode> children = node.getChildren(); - assertEquals(numSiblings, children.size()); - TreeMapNode.sort(children); - - double weight = children.get(0).getWeight(); - for(int i = 1; i < numSiblings - 1; i++) { - double currentWeight = children.get(i).getWeight(); - //check that the ratio between node weights is approximately 2 (which is the BASE) - assertEquals(BASE, weight/currentWeight, DELTA); - weight = currentWeight; - } - } -}
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ValueFormatterTest.java Wed Jun 01 15:58:04 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class ValueFormatterTest { - - private ValueFormatter formatter; - - @Test - public final void getFormattedWeight() { - formatter = new ValueFormatter(2); - assertEquals("2.00 Bytes", formatter.format()); - - formatter = new ValueFormatter(2222); - assertEquals("2.22 KBytes", formatter.format()); - - formatter = new ValueFormatter(2222222); - assertEquals("2.22 MBytes", formatter.format()); - } -} \ No newline at end of file