GEMLayoutAlgorithm.java

Index Score
org.jgraph.plugins.layouts
JGraph

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
LOOPSNumber of loops
DECL_COMMENTSComments in declarations
EXEC_COMMENTSComments in executable code
SIZESize of the file in bytes
COMMENTSComment lines
DOC_COMMENTNumber of javadoc comment lines
COMPARISONSNumber of comparison operators
JAVA0034JAVA0034 Missing braces in if statement
LINE_COMMENTNumber of line comments
OPERANDSNumber of operands
PROGRAM_LENGTHHalstead program length
LINESNumber of lines in the source file
OPERATORSNumber of operators
LOGICAL_LINESNumber of statements
CYCLOMATICCyclomatic complexity
ELOCEffective lines of code
JAVA0035JAVA0035 Missing braces in for statement
LOCLines of code
UNIQUE_OPERANDSNumber of unique operands
WHITESPACENumber of whitespace lines
PROGRAM_VOCABHalstead program vocabulary
EXITSProcedure exits
RETURNSNumber of return points from functions
BLOCKSNumber of blocks
JAVA0068JAVA0068 Modifiers not declared in recommended order
INTERFACE_COMPLEXITYInterface complexity
JAVA0270JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
FUNCTIONSNumber of function declarations
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0130JAVA0130 Non-static method does not use instance fields
PARAMSNumber of formal parameter declarations
JAVA0119JAVA0119 Control variable changed within body of for loop
JAVA0144JAVA0144 Line exceeds maximum M characters
UNIQUE_OPERATORSNumber of unique operators
JAVA0049JAVA0049 Nested block at depth N (maximum: M)
BLOCK_COMMENTNumber of block comment lines
JAVA0143JAVA0143 Synchronized method
NEST_DEPTHMaximum nesting depth
PROGRAM_VOLUMEHalstead program volume
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0266JAVA0266 Use of System.out
JAVA0174JAVA0174 Assigned local variable never used
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0265JAVA0265 Use of Throwable.printStackTrace()
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0100JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0254JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0267JAVA0267 Use of System.err
JAVA0075JAVA0075 Method parameter hides field
/* * @(#)GEMLayoutAlgorithm.java 1.0 18-MAY-2004 * * Copyright (c) 2004, Winkler * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jgraph.plugins.layouts; import java.awt.Color; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Properties; import org.jgraph.JGraph; import org.jgraph.event.GraphModelEvent; import org.jgraph.event.GraphModelListener; import org.jgraph.graph.AttributeMap; import org.jgraph.graph.CellMapper; import org.jgraph.graph.CellView; import org.jgraph.graph.EdgeView; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphModel; import org.jgraph.graph.VertexView; import org.jgraph.plugins.layouts.AnnealingLayoutAlgorithm.MathExtensions; /** * <h1>GEM Layout Algorithm</h1> * <p> * Based on the work of Arne Frick, Andreas Ludwig, Heiko Mehldau: * "A Fast Adaptive Layout Algorithm for Undirected Graphs"; * Extended Abstract and System Demonstration; Faculty of Informatik of * the University Karlsruhe; 1994 * <p> * This Algorithm works by giving every cell a position and a temperature. * Then for every cell forces are computed. Every other cell repulses the * actual calculated cell away. On the other hand, cells, connected by edges * are attracted, until a minimum distance is reached. The result of this * forces is a move of the position of the actual cell in the direction of * the force and with the length of the temperature. Then the temperature * will be decreased, if the last impulse and the current impulse looks like a * rotation or a oscillation. this is done for every cell until the temperature * of all cells or the average of the temperature of every cell is until a * given minimum value or a maximum of rounds is reached. * @author winkler */ public class GEMLayoutAlgorithm extends JGraphLayoutAlgorithm implements GraphModelListener { /** * Key used on every cell. This key indicates that in the cell are * attributes stored by this algorithm. The algorithm itself never asks for * this key. This is for others developers only, using this algorithm, to * find out, where sometimes approaching attributes come from. */ public final static String KEY_CAPTION = "GEM-TEMPORARY-DATA"; /** * Key used on every cell. Under this key every cell stores temporary the * temperature of itself. Temperature is a indicator how far a cell can move * and all temperatures together indicating how long the algorithm will run. */ public final static String KEY_TEMPERATURE = "Temperature"; /** * Key used on every cell. Under this key every cell stores temporary the * current force impulse affecting the cell. */ public final static String KEY_CURRENT_IMPULSE = "Current_Impulse"; /** * Key used on every cell. Under this key every cell stores temporary the * last impulse. This is the value of the previous * {@link #KEY_CURRENT_IMPULSE}. */ public final static String KEY_LAST_IMPULSE = "Last_Impulse"; /** * Key used on every cell. Under this key every cell stores the temporary * position on the display, while the calculation is running. This makes * the algorithm faster, than asking the Cell everytime for its position. * So the algorithm can anytime be canceled, whithout changing something. */ public final static String KEY_POSITION = "Position"; /** * Key used on every cell. Under this key every cell stores the temporary * skew gauge. This value is for punish rotations of the cells. */ public final static String KEY_SKEWGAUGE = "Skew_Gauge"; /** * Key used on every Cell. Under this key every cell stores the cells, * that are only one edge away from it. The relatives are stored, after the * first time, they are desired and calculated by * {@link #getRelatives(CellView)}. */ public final static String KEY_RELATIVES = "Relatives"; /** * Key used on every Cell. This indicates a weight for the number of edges * received by {@link #getNodeWeight(CellView)} */ public final static String KEY_MASSINDEX = "Mass_Index"; /** * Key used only with clusters. Under this key a cluster has an ArrayList. * This list is filled with the clustered vertices. * @see #clusterGraph() */ public final static String KEY_CLUSTERED_VERTICES = "Clustered Vertices"; /** * Key used only with clusters. Under this key vertices have the cluster * they belong to. * @see #clusterGraph() */ public final static String KEY_CLUSTER = "Cluster"; /** * Key used only with clusters. Under this key a cluster has a boolean value * indicating that this vertice is a cluster (clusters are * VertexView-instances like every other cell). * @see #clusterGraph() */ public final static String KEY_IS_CLUSTER = "is Cluster"; /** * Key used only with clusters. Under this key every cluster has a position, * which represents the position of the cluster, right after the clustering * process. After the layout update process is finished, the move, resulting * of subtracting the position under {@link #KEY_POSITION} from the * position under this value, will be performed to all vertices in the * cluster. By holding the initial position here clustering becomes * possible. * * @see #clusterGraph() * @see #declusterGraph() */ public final static String KEY_CLUSTER_INIT_POSITION = "initial Position of the Cluster"; /** * List of all nodes in the graph. */ private ArrayList cellList; /** * List of all nodes the algorithm should be done for. */ private ArrayList applyCellList; /** * List of all edges in the graph. This is only needed for the optimization * Algorithm. */ private ArrayList edgeList; /** * needed for comperation with other double values if they are 0.0. */ private double equalsNull = 0.00000000000000001; /** * starting value for the temperatures of the cells */ protected double initTemperature = 10; /** * if the temperature of all cells or the average of the temperatures of * all cells is below this value, the algorithm stops */ protected double minTemperature = 3; /** * temperature will never be over this value */ protected double maxTemperature = 256; /** * the length of the Edges in Pixel, the algorithm tries to keep */ protected double prefEdgeLength = 100; /** * the strength of the gravitation force, directed to the barycenter of the * graph, added to all cells. */ protected double gravitation = 0.0625; /** * length of a force vector with a random direction, added to all cells. */ protected double randomImpulseRange = 32; /** * opening angle in radiant, that detects oscillations */ protected double alphaOsc = Math.toRadians(90); /** * opening angle in radiant, that detects rotations */ protected double alphaRot = Math.toRadians(60); /** * penalty value for a detected oscillation */ protected double sigmaOsc = 1.0/3.0; /** * penalty value for a detected rotation */ protected double sigmaRot = 1.0/2.0; /** * number of rounds until the algorithm will break. This value is * precalculated to a aproximativ value of 4 times the count of Cells in * {@link #applyCellList} */ private int maxRounds; /** * counts the rounds */ private int countRounds; /** * If the pathlength between an inserted cell and an allready layouted cell * is below this value, the allready layouted cell will be layouted again. */ private int recursionDepth; /** * Describes the distance around a cell, that will be whatched for other * cells, intersecting this area. If another cell intersects, a force will * be added to the cell, that pushes it away. */ private double overlapDetectWidth; /** * Describes a distance the algorithm tries to keep, when he detects a * overlapping cell. */ private double overlapPrefDistance; /** * switches the feature to whatch for overlapping on/off */ private boolean avoidOverlapping; /** * describes, what method will be taken, when cells are inserted. Posible * values are */ private String layoutUpdateMethod; /** * condition for method isFrozen(). decides whether the method returns true * when the average of all temperatures or all temperatures are below * {@link #minTemperature}. */ private boolean shouldEndPerAverage; /** * condition for the method calculate(). decides whether the algorithm * is computed for the cellViews every time in the same sequence * or if the cellViews are computed every time in a random sequence. */ private boolean shouldComputePermutation; /** * Switches the skill of the algorithm to perform the layout update process */ private boolean isActive = true; /** * Checks if the algorithm is currently running. If this is the case, no * GraphModelEvent will be computed and no new run can be initiated. */ private boolean isRunning = false; /** * a reference to the instance of jgraph */ private JGraph jgraph; /** * the gpConfiguration of this algorithm */ protected Properties config; /** * to identify for the method {@link #loadRuntimeValues(int)}, that the * algorithm wants to perform a new run */ protected final static int VALUES_PUR = 0; /** * to identify for the method {@link #loadRuntimeValues(int)}, that the * algorithm wants to perform a layout update */ protected final static int VALUES_INC = 1; /** * algorithm used for optimizing the result of this algorithm */ private AnnealingLayoutAlgorithm optimizationAlgorithm; /** * switches the usage of the optimizing algorithm */ private boolean useOptimizeAlgorithm; /** * gpConfiguration of the optimizing algorithm */ private Properties optimizationAlgorithmConfig; /** * Switches clustering for the layout update process on/off */ private boolean isClusteringEnabled; /** * The initial temperature for clusters. It is recommended, that this value * is lower than {@link #initTemperature} to get a good looking layout */ private double clusterInitTemperature; /** * Scales forces, that are effecting clusters. It is recommendet to take * a value between 1.0 and 0.0. This garanties, that clusters move slower * than other cells. That rises the chance of getting a good looking layout * after the calculation. */ private double clusterForceScalingFactor; /** * Effects, how many clusters are created, when the layout update process * starts. This affects the initial number of clusters, which is the number * of cells available minus the number of cells to layout. The result of * that term is divided by this factor, to get the maximum number of * clusters. After this calculation, the clustering algorithm tries to * minimize the number of clusters, so there might be less clusters than * the maximum number. */ private double clusteringFactor; /** * The initial size for the layout update method perimeter. This describes * a radius around an inserted cell. Every other inserted cell in this * radius increases the radius by {@link #perimeterSizeInc}. After finishing * increasing the radius, every cell, from the cells, that are already * layouted, in the radius is added to the list of cells, that'll gain a * new position during the layout update process. This should bring up * the behaviour, that the previous layouted cells make space for the layout * of the inserted cells. */ private double perimeterInitSize; /** * Inserted cells whithin a radius of {@link #perimeterInitSize} around * a inserted cell are counted. After counting the inserted cells around * a inserted cell, the initial radius is increased by this increase value * times the number of inserted cells around the inserted cell. Every * previous layouted cell in the resulting radius around the inserted cell * is going to be layouted again. */ private double perimeterSizeInc; // private boolean isDebugging = false; /******************************************************************************/ /** * Constructs a new GEM Layout Algorithm. */ public GEMLayoutAlgorithm(AnnealingLayoutAlgorithm optimizer){ cellList = new ArrayList(); applyCellList = new ArrayList(); edgeList = new ArrayList(); optimizationAlgorithm = optimizer; } /** * Returns the name of this algorithm in human * readable form. */ public String toString() { return "GEM"; } /** * Get a human readable hint for using this layout. */ public String getHint() { return "Ignores selection"; } /** * Returns an new instance of SugiyamaLayoutSettings */ public JGraphLayoutSettings createSettings() { return new GEMLayoutSettings(this); } /******************************************************************************/ /** * Starts the Calculation of a new layout with the GEM-Algorithm * @param graph JGraph instance * @param dynamic_cells List of all nodes the layout should move * @param static_cells List of node the layout should not move but allow for * @see #initialize() * @see #calculate() */ public void run(JGraph graph, Object[] dynamic_cells, Object[] static_cells) { isRunning = true; jgraph = graph; jgraph.getModel().addGraphModelListener(this); cellList = new ArrayList(); applyCellList = new ArrayList(); //extracting the nodes from jgraph, the algorithm should be performed on getNodes(jgraph, dynamic_cells); // long starttime = System.currentTimeMillis(); //ALGORITHM START boolean isCanceled = initialize(); // initializes algorithm // sets the startvalues in cells if( !isCanceled ) isCanceled = calculate();//performs the algorithm on the cells //ALGORITHM END if( !isCanceled && useOptimizeAlgorithm ) optimizationAlgorithm.performOptimization ( applyCellList, cellList, edgeList, optimizationAlgorithmConfig); if( !isCanceled ) correctCoordinates(); //sets the calculated data into cellView's bounds if not canceled if( !isCanceled ) isCanceled = setNewCoordinates(jgraph); //removes the temporary data, stored by the algorithm, from the nodes removeTemporaryLayoutDataFromCells(); isRunning = false; } /******************************************************************************/ /** * Loads the actual desired values from the {@link #config gpConfiguration} to * the fields, where they are used later. * * @param valueID {@link #VALUES_PUR} for a normal run or {@link #VALUES_INC} * for a layout update process. */ protected void loadRuntimeValues(int valueID){ maxRounds = applyCellList.size() * 4;//estimated value; reached rarely countRounds = 0;//start value; counts the rounds in calculate() isActive = isTrue((String) config.get(GEMLayoutSettings.KEY_LAYOUT_UPDATE_ENABLED)); recursionDepth = Integer.parseInt((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_DEPTH)); layoutUpdateMethod = (String) config.get(GEMLayoutSettings.KEY_LAYOUT_UPDATE_METHOD); if( valueID == VALUES_PUR ){ initTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_INIT_TEMPERATURE)); minTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_MIN_TEMPERATURE)); maxTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_MAX_TEMPERATURE)); prefEdgeLength = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_PREF_EDGE_LENGTH)); gravitation = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_GRAVITATION)); randomImpulseRange = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_RANDOM_IMPULSE_RANGE)); overlapDetectWidth = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_OVERLAPPING_DETECTION_WIDTH)); overlapPrefDistance = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_OVERLAPPING_PREF_DISTANCE)); shouldEndPerAverage = isTrue((String)config.get( GEMLayoutSettings.KEY_COMPUTE_PERMUTATION)); shouldComputePermutation = isTrue((String)config.get( GEMLayoutSettings.KEY_END_CONDITION_AVERAGE)); avoidOverlapping = isTrue((String)config.get( GEMLayoutSettings.KEY_AVOID_OVERLAPPING)); alphaOsc = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_ALPHA_OSC)); alphaRot = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_ALPHA_ROT)); sigmaOsc = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_SIGMA_OSC)); sigmaRot = Double.parseDouble((String)config.get( //gets 1/x GEMLayoutSettings.KEY_SIGMA_ROT)); useOptimizeAlgorithm = isTrue((String)config.get( GEMLayoutSettings.KEY_OPTIMIZE_ALGORITHM_ENABLED)); optimizationAlgorithmConfig = (Properties) config.get( GEMLayoutSettings.KEY_OPTIMIZE_ALGORITHM_CONFIG); } else if( valueID == VALUES_INC ){ initTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_INIT_TEMPERATURE)); minTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_MIN_TEMPERATURE)); maxTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_MAX_TEMPERATURE)); prefEdgeLength = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_PREF_EDGE_LENGTH)); gravitation = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_GRAVITATION)); randomImpulseRange = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_RANDOM_IMPULSE_RANGE)); overlapDetectWidth = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_OVERLAPPING_DETECTION_WIDTH)); overlapPrefDistance = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_OVERLAPPING_PREF_DISTANCE)); shouldEndPerAverage = isTrue((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_COMPUTE_PERMUTATION)); shouldComputePermutation = isTrue((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_END_CONDITION_AVERAGE)); avoidOverlapping = isTrue((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_AVOID_OVERLAPPING)); alphaOsc = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_ALPHA_OSC)); alphaRot = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_ALPHA_ROT)); sigmaOsc = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_SIGMA_OSC)); sigmaRot = Double.parseDouble((String)config.get( //gets 1/x GEMLayoutSettings.KEY_LAYOUT_UPDATE_SIGMA_ROT)); useOptimizeAlgorithm = isTrue((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_OPTIMIZE_ALGORITHM_ENABLED)); optimizationAlgorithmConfig = (Properties) config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_OPTIMIZE_ALGORITHM_CONFIG); perimeterInitSize = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_METHOD_PERIMETER_INIT_SIZE)); perimeterSizeInc = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_METHOD_PERIMETER_SIZE_INC)); isClusteringEnabled = isTrue((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_CLUSTERING_ENABLED)); clusterInitTemperature = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_CLUSTERING_INIT_TEMPERATURE)); clusterForceScalingFactor = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_CLUSTERING_FORCE_SCALING_FACTOR)); clusteringFactor = Double.parseDouble((String)config.get( GEMLayoutSettings.KEY_LAYOUT_UPDATE_CLUSTERING_FACTOR)); } //with that line sigmaRot will be 1/(x*cellCount) with x is configurable sigmaRot *= 1.0 / (applyCellList.size() == 0 ? 1 : applyCellList.size()); } /******************************************************************************/ /** * Helping method. Transforms a String, containing only the characters "true" or * "false", regardless if upper or lower case, into a boolean value. * * @param boolValue String containing a boolean value * @return boolean value represented by the given string */ protected boolean isTrue(String boolValue){ if( boolValue != null ){ if( "TRUE".equals(boolValue.toUpperCase()) ){ return true; } else if( "FALSE".equals(boolValue.toUpperCase()) ){ return false; } } return false; } /******************************************************************************/ /** * Extracts the Cells from JGraph and fills {@link #applyCellList}, * {@link #cellList} and {@link #edgeList}. If applyToAll is * <b><code>false</code></b> only in jgraph selected cells are added to * {@link #applyCellList} else all cells are added. * * @param jgraph actual instance of jgraph * cells or only on the selected. */ private void getNodes(JGraph jgraph, Object[] cells){ Object[] all = jgraph.getRoots(); CellView[] view = jgraph.getGraphLayoutCache().getMapping(all,false); CellView[] selectedView = jgraph.getGraphLayoutCache().getMapping( cells,false); for (int i = 0; i < view.length; i++) if (view[i] instanceof VertexView){ cellList.add(view[i]); applyCellList.add(view[i]); } else if( view[i] instanceof EdgeView ){ edgeList.add(view[i]); } for( int i = 0; i < selectedView.length; i++ ) if( selectedView[i] instanceof VertexView ) applyCellList.add(selectedView[i]); } /******************************************************************************/ /** * Sets the initial Values, gained from the {@link #config gpConfiguration} * into the Cells. * * @return Because the progress dialog is allready visible during the * initialisation, <b><code>true</code><b> is returned when cancel is pressed * on it. */ private boolean initialize(){ int length = cellList.size(); for( int i = 0; i < length; i++ ){ CellView view = (CellView) cellList.get(i); initializeVertice(view); } for( int i = 0; i < applyCellList.size(); i++ ) computeLastImpulse( (CellView) applyCellList.get(i) ); return false; //not canceled } /******************************************************************************/ /** * Sets the initial values for one Cell. * * @param view Cell, the initial values should be set for. */ private void initializeVertice(CellView view){ Map attributes = view.getAttributes(); if( attributes == null ) attributes = new Hashtable(); attributes.put(KEY_CAPTION,KEY_CAPTION); initPosition(view); if( isCluster(view) ){ attributes.put(KEY_TEMPERATURE, new Double(clusterInitTemperature)); } else attributes.put(KEY_TEMPERATURE, new Double(initTemperature)); attributes.put(KEY_SKEWGAUGE, new Double(0.0)); attributes.put(KEY_CURRENT_IMPULSE,new Point2D.Double()); attributes.put(KEY_LAST_IMPULSE ,new Point2D.Double()); } /******************************************************************************/ /** * Runs the algorithm. First a running sequence is initialised. If * shouldComputePermuation is <b><code>true</code><b> then a new * permutation is computed for every round, else a single determined sequence is * established. After this for every Cell a current impulse is calculated, * position and temperature is updated. This is done, until the graph is frozen, * a maximum on rounds is reached or cancel on the progress dialog is pressed. * * @return <b><code>true</code></b> when cancel on the progress dialog is * pressed * @see #computeCurrentImpulse(CellView) * @see #createPermutation(int) * @see #isFrozen() * @see #updatePosAndTemp(CellView) */ private boolean calculate(){ int length = applyCellList.size(); int[] sequence = new int[length]; boolean isCanceled = false; //case no permutation is desired, the series is computed one time only if( !shouldComputePermutation ) //else is in the loop below for( int i = 0; i < length; i++ ) sequence[i] = i; while( !isFrozen() && countRounds <= maxRounds && (!isCanceled) ){ //case permutation is desired, it's calculated every round if( shouldComputePermutation ) sequence = createPermutation(length); //loop over all nodes (order is in sequence) for( int i = 0; i < sequence.length; i++ ){ CellView view = (CellView) applyCellList.get(sequence[i]); computeCurrentImpulse(view); //computes direction of impulse updatePosAndTemp(view); //computes new position and temperature } countRounds++; } return false; } /******************************************************************************/ /** * Calculates the current impulse for the given cell. * * @param view Cell, the current impulse should be calculated * @see #computeImpulse(CellView) */ private void computeCurrentImpulse(CellView view){ //gets the impulse for view Point2D.Double impulse = computeImpulse(view); //set result into node view.getAttributes().put(KEY_CURRENT_IMPULSE,impulse); } /******************************************************************************/ /** * Calculates the last impulse for the given cell. This is only nesessary while * initializing the cells. * * @param view Cell, the last impulse should be calculated * @see #computeImpulse(CellView) */ private void computeLastImpulse(CellView view){ //gets the impulse for view Point2D.Double impulse = computeImpulse(view); //set result into node view.getAttributes().put(KEY_LAST_IMPULSE,impulse); } /******************************************************************************/ /** * Computes an Impulse representing a Force affecting the position of the given * Cell. This impulse consists of a attracting force, pulling the cell to the * barycenter of all cells, a pulse with user defined length and random * direction, a force repulsing cells from each other, a force attracting * connected cells together, and, as a additional feature, a force, repulsing * the current cell from overlapping cells. * * @param view Cell, the impulse should be computed * @return impulse, transformed in a Point2D.Double-Instance. */ private Point2D.Double computeImpulse(CellView view){ Point2D.Double impulse = new Point2D.Double(); Point2D.Double pos = getPosition(view); // boolean isCellACluster = isCluster(view); //the more edges a cell have, the heavier the cell is double massIndex = getNodeWeight(view); //gets the barycenter of all cells Point2D.Double barycenter = computeBarycenter(cellList); //attracting force from the barycenter to every cell Point2D.Double gravitationForce = new Point2D.Double( (barycenter.getX() - pos.getX()) * gravitation * massIndex, (barycenter.getY() - pos.getY()) * gravitation * massIndex); //random glitch is added to force Point2D.Double randomImpulse = getRandomVector(randomImpulseRange); //repulsive Forces //from all nodes ArrayList repulsiveForce = new ArrayList(); for( int i = 0 ; i < cellList.size(); i++ ) if( cellList.get(i) != view ){//all cells except the actual view // CellView uView = (CellView) cellList.get(i); // if( !isCluster(uView)){ Point2D.Double uPos = getPosition(i,cellList); double deltaX = (pos.getX() - uPos.getX()); double deltaY = (pos.getY() - uPos.getY()); /* double sgnX = MathExtensions.sgn(deltaX); double sgnY = MathExtensions.sgn(deltaY); if( isCellACluster && isCluster(uView) ){ deltaX -= uView.getBounds().getWidth() / 2.0; deltaY -= uView.getBounds().getHeight()/ 2.0; } if( isCellACluster ){ deltaX -= view.getBounds().getWidth() / 2.0; deltaY -= view.getBounds().getHeight()/ 2.0; } if( sgnX != MathExtensions.sgn(deltaX) ){ deltaX *= sgnX; } if( sgnY != MathExtensions.sgn(deltaY) ){ deltaY *= sgnY; } */ double absDelta = MathExtensions.abs(deltaX,deltaY); if( absDelta > equalsNull ){ repulsiveForce.add(new Point2D.Double( deltaX * ((prefEdgeLength * prefEdgeLength) / (absDelta * absDelta)), deltaY * ((prefEdgeLength * prefEdgeLength) / (absDelta * absDelta)))); } // } } //attractive Forces: //from all nodes that have an edge with view ArrayList relatives = getRelativesFrom(cellList,view); ArrayList attractiveForce = new ArrayList(relatives.size()); for( int i = 0; i < relatives.size(); i++ ){ // CellView child = (CellView) relatives.get(i); Point2D.Double cPos = getPosition(i,relatives); double deltaX = (pos.getX() - cPos.getX()); double deltaY = (pos.getY() - cPos.getY()); /* double sgnX = MathExtensions.sgn(deltaX); double sgnY = MathExtensions.sgn(deltaY); if( isCellACluster && isCluster( child )){ deltaX -= child.getBounds().getWidth() / 2.0; deltaY -= child.getBounds().getHeight()/ 2.0; } if( isCellACluster ){ deltaX -= view.getBounds().getWidth() / 2.0; deltaY -= view.getBounds().getHeight()/ 2.0; } if( sgnX != MathExtensions.sgn(deltaX) ) deltaX *= sgnX; if( sgnY != MathExtensions.sgn(deltaY) ) deltaY *= sgnY; */ double absDelta = MathExtensions.abs(deltaX,deltaY); attractiveForce.add(new Point2D.Double( deltaX * (( absDelta * absDelta ) / ( prefEdgeLength * prefEdgeLength * massIndex )), deltaY * (( absDelta * absDelta ) / ( prefEdgeLength * prefEdgeLength * massIndex )))); } /* the next part is NOT part of the original algorithm */ /* it adds a force if the actual cell overlapps another cell */ ArrayList forcesByOverlapping = new ArrayList(); if( avoidOverlapping ){ Rectangle viewBounds = new Rectangle( (int)pos.x, (int)pos.y, (int)view.getBounds().getWidth(), (int)view.getBounds().getHeight()); Rectangle viewBorder = new Rectangle( (int)(viewBounds.getX()-overlapDetectWidth), (int)(viewBounds.getY()-overlapDetectWidth), (int)(viewBounds.getWidth()+(2.0*overlapDetectWidth)), (int)(viewBounds.getHeight()+(2.0*overlapDetectWidth))); for( int i = 0; i < cellList.size(); i++ ){ Point2D.Double uPos = getPosition(i,cellList); Rectangle uBounds = new Rectangle( (int)uPos.x, (int)uPos.y, (int)((CellView)cellList.get(i)).getBounds().getWidth(), (int)((CellView)cellList.get(i)).getBounds().getHeight()); if( view != cellList.get(i) && viewBorder.intersects(uBounds) ){ Dimension viewSize = viewBounds.getSize(); Dimension uSize = uBounds.getSize(); double minDistance = (Math.max(viewSize.getWidth(),viewSize.getHeight())/2.0)+ (Math.max(uSize.getWidth(),uSize.getHeight())/2.0)+overlapPrefDistance; double deltaX = (pos.x - uPos.x); double deltaY = (pos.y - uPos.y); /* if( isCellACluster ){ deltaX -= view.getBounds().getWidth() / 2.0; deltaY -= view.getBounds().getHeight()/ 2.0; } if( isCluster((CellView)cellList.get(i))){ deltaX -= ((CellView)cellList.get(i)).getBounds().getWidth() / 2.0; deltaY -= ((CellView)cellList.get(i)).getBounds().getHeight()/ 2.0; }*/ if( deltaX < equalsNull && deltaX >= 0.0 ){ deltaX = equalsNull; } else if( deltaX > -equalsNull && deltaX <= 0.0 ){ deltaX = -equalsNull; } if( deltaY < equalsNull && deltaY >= 0.0 ){ deltaY = equalsNull; } else if( deltaY > -equalsNull && deltaY <= 0.0 ){ deltaY = -equalsNull; } double absDelta = MathExtensions.abs(deltaX,deltaY); Point2D.Double force = new Point2D.Double( deltaX*(minDistance*minDistance)/(absDelta*absDelta), deltaY*(minDistance*minDistance)/(absDelta*absDelta)); // System.out.println("Overlapping Nodes: ("+pos.x+"|"+pos.y+") and ("+uPos.x+"|"+uPos.y+") -> Distance = "+absDelta+" -> Force = "+force); forcesByOverlapping.add(force); } } } ArrayList additionalForce = new ArrayList(); //adding the forces impulse = add(impulse,gravitationForce); impulse = add(impulse,randomImpulse); for( int i = 0; i < repulsiveForce.size(); i++ ) impulse = add(impulse,(Point2D.Double) repulsiveForce.get(i) ); for( int i = 0; i < attractiveForce.size(); i++ ) impulse = sub(impulse,(Point2D.Double) attractiveForce.get(i) ); for( int i = 0; i < forcesByOverlapping.size(); i++ ) impulse = add(impulse,(Point2D.Double) forcesByOverlapping.get(i)); for( int i = 0; i < additionalForce.size(); i++ ) impulse = add(impulse,(Point2D.Double) additionalForce.get(i) ); return impulse; } /******************************************************************************/ /** * Updating the position of the given cell, by taking the direction of the * current impulse and the length of the temperature of the cell. After this * temperature will fall, when the last impulse and the current impulse are * part of a rotation or a oscillation, temperature of the cell will be * decreased. * * @param view Cell that should be updated */ private void updatePosAndTemp(CellView view){ Point2D.Double impulse = (Point2D.Double) view.getAttributes().get(KEY_CURRENT_IMPULSE); Point2D.Double lastImpulse = (Point2D.Double) view.getAttributes().get(KEY_LAST_IMPULSE); Point2D.Double position = getPosition(view); double localTemperature = ((Double) view.getAttributes().get(KEY_TEMPERATURE)).doubleValue(); double skewGauge = ((Double) view.getAttributes().get(KEY_SKEWGAUGE)).doubleValue(); double absImpulse = MathExtensions.abs(impulse); double absLastImpulse = MathExtensions.abs(lastImpulse); if( absImpulse > equalsNull ){ //if impulse != 0 //scaling with temperature if( isCluster(view) ){ impulse.setLocation( impulse.getX() * localTemperature * clusterForceScalingFactor / absImpulse, impulse.getY() * localTemperature * clusterForceScalingFactor / absImpulse); } else { impulse.setLocation( impulse.getX() * localTemperature / absImpulse, impulse.getY() * localTemperature / absImpulse); } view.getAttributes().put(KEY_CURRENT_IMPULSE,impulse); position.setLocation(position.getX()+impulse.getX(), position.getY()+impulse.getY()); view.getAttributes().put(KEY_POSITION,position); /* if( isDebugging ){ check(impulse,"impulse12"); check(position,"position12"); }*/ } if( absLastImpulse > equalsNull ){ //beta = angle between new and last impulse double beta = MathExtensions.angleBetween(impulse,lastImpulse); double sinBeta = Math.sin(beta); double cosBeta = Math.cos(beta); //detection for rotations if( Math.abs(sinBeta) >= Math.sin((Math.PI/2.0)+(alphaRot/2.0)) ) skewGauge += sigmaRot * MathExtensions.sgn(sinBeta); //detection for oscillation if( cosBeta < Math.cos(Math.PI+(alphaOsc/2.0)) ) localTemperature *= sigmaOsc * Math.abs(cosBeta); localTemperature *= 1.0 - Math.abs(skewGauge); localTemperature = Math.min(localTemperature,maxTemperature); } //applying changes view.getAttributes().put(KEY_TEMPERATURE,new Double(localTemperature)); view.getAttributes().put(KEY_POSITION ,position); view.getAttributes().put(KEY_SKEWGAUGE ,new Double(skewGauge)); view.getAttributes().put(KEY_LAST_IMPULSE,new Point2D.Double( impulse.getX(), impulse.getY())); /* if( isDebugging ) checkCellList();*/ } /******************************************************************************/ /** * Adding two forces. * @param v1 Force that should be added with v2 * @param v2 Force that should be added with v1 * @return Sum of both forces. */ private Point2D.Double add(Point2D.Double v1, Point2D.Double v2){ return new Point2D.Double(v1.getX()+v2.getX(),v1.getY()+v2.getY()); } /******************************************************************************/ /** * Subtracing two forces. * @param v1 Force, v2 should be subtracted from * @param v2 Force, that should be subtracted from v1. */ private Point2D.Double sub(Point2D.Double v1, Point2D.Double v2){ return new Point2D.Double(v1.getX()-v2.getX(),v1.getY()-v2.getY()); } /******************************************************************************/ /** * Returns all Cells, that have a direct connection with the given cell and are * a member of the given list. * * @param list List of some cells, that should contain some relatives from view * @param view Cell, the relatives are requested from * @return List of all relatives that are in list. * @see #getRelatives(CellView) */ private ArrayList getRelativesFrom(ArrayList list, CellView view){ ArrayList relatives = getRelatives(view); ArrayList result = new ArrayList(); for( int i = 0; i < relatives.size(); i++ ) if( list.contains(relatives.get(i)) ) result.add(relatives.get(i)); return result; } /******************************************************************************/ /** * Returns a list of all cells, that have a direct connection with the given * cell via a edge. At the end of this method, the result is stored in the given * cell, so it will be available the next time, the method runs. This temporary * stored data will stay there, until the algorithm finishes * (successfull or not). * * @param view Cell, the relatives requested from. * @return List of all cells, that have a direct connection with a edge to * the given cell. */ private ArrayList getRelatives(CellView view){ if( !(view instanceof VertexView) ) { new Exception("getRelatives 1").printStackTrace(); return null; } if( view.getAttributes().containsKey(KEY_RELATIVES) ) return (ArrayList) view.getAttributes().get(KEY_RELATIVES); ArrayList relatives = new ArrayList(); //if view is a cluster, then all clustered cells are extracted and //getRelatives is called for every cell again. the resulting relatives //are checked, if they are in the same cluster or another cluster. //if the last condition is the case, the cluster is added, else the //vertex is added to the list of relatives, iff he isn't already in the //list if( isCluster(view) ){ ArrayList clusteredVertices = (ArrayList) view.getAttributes().get(KEY_CLUSTERED_VERTICES); for( int i = 0; i < clusteredVertices.size(); i++ ){ ArrayList vertexRelatives = getRelatives( (CellView)clusteredVertices.get(i)); for( int j = 0; j < vertexRelatives.size(); j++ ){ CellView relative = (CellView) vertexRelatives.get(j); if( !clusteredVertices.contains(relative) ){ /* if( relative.getAttributes().containsKey(KEY_CLUSTER) ){ relative = (CellView) relative.getAttributes().get(KEY_CLUSTER); }*/ if( !relatives.contains(relative)) relatives.add(relative); } } } } else { //runs only for vertices. finds all ports of the vertex. every //edge in every port is checked on their source and target. //the one, that isn't the vertex, we are searching the relatives of, //is added to the list of relatives. ArrayList portsCells = new ArrayList(); VertexView vertexView = (VertexView)view; GraphModel model = jgraph.getModel(); CellMapper mapper = jgraph.getGraphLayoutCache() ; Object vertexCell = vertexView.getCell() ; for (int i = 0; i < model.getChildCount(vertexCell); i++){ Object portCell = model.getChild(vertexCell, i); portsCells.add(portCell); } for( int i = 0; i < portsCells.size() ; i++ ){ Object portCell = portsCells.get(i); Iterator edges = model.edges(portCell); while (edges.hasNext() ){ Object edge = edges.next() ; Object nextPort = null; if( model.getSource(edge) != portCell ){ nextPort = model.getSource(edge); } else { nextPort = model.getTarget(edge); } CellView nextVertex = mapper.getMapping( model.getParent(nextPort), true); relatives.add(nextVertex); } } } view.getAttributes().put(KEY_RELATIVES,relatives); return relatives; } /******************************************************************************/ /** * This is a rating method for the cells. It is used during * {@link #computeImpulse(CellView)} scale some forces. * * @param view Cell, the weight is of interest. */ //TODO: method doesn't work right for clusters private double getNodeWeight(CellView view){ if( view.getAttributes().containsKey(KEY_MASSINDEX) ) return ((Double)view.getAttributes().get(KEY_MASSINDEX)). doubleValue(); int childCount = getRelatives(view).size(); double massIndex = (childCount + 1) / 2.0; view.getAttributes().put(KEY_MASSINDEX,new Double(massIndex)); return massIndex; } /******************************************************************************/ /** * Applies the changes to the Cells. This means, that all temporary stored * positions are applied to all cells in {@link #applyCellList} */ private boolean setNewCoordinates(JGraph jgraph){ Map viewMap = new Hashtable(); for( int i = 0; i < cellList.size(); i++ ){ Point2D.Double pos = getPosition(i,cellList); Rectangle2D r = ((CellView)cellList.get(i)).getBounds(); r.setFrame(pos.getX() - (r.getWidth() /2.0), pos.getY() - (r.getHeight()/2.0), r.getWidth(), r.getHeight()); Object cell = ((CellView) cellList.get(i)).getCell(); Map map = new Hashtable(); GraphConstants.setBounds(map, r); viewMap.put(cell, map); } jgraph.getGraphLayoutCache().edit(viewMap,null,null,null); return false; } /******************************************************************************/ /** * Clears the temporary data from the cells in {@link #cellList} (all cells). */ private void removeTemporaryLayoutDataFromCells(){ for( int i = 0; i < cellList.size(); i++ ) ((CellView)cellList.get(i)).getAttributes().clear(); } /******************************************************************************/ /** * Checks, if the algorithm could break it's calculation earlier, than * performing until {@link #countRounds} is {@link #maxRounds}. This depends on * {@link #shouldEndPerAverage}. * */ private boolean isFrozen(){ double sumOfTemp = 0.0; //sum of temperatures to get the average value double globalTemp = 0.0; //average value of all temperatures boolean isFrozen = true;//true while all temperatures <= minTemperature for( int i = 0; i < applyCellList.size(); i++ ){ double temperature = getTemperature(i,applyCellList); sumOfTemp += temperature; isFrozen = isFrozen && (temperature <= minTemperature); if( !isFrozen && !shouldEndPerAverage )//speeds up a little break; } if( shouldEndPerAverage ){ globalTemp = sumOfTemp / applyCellList.size(); return globalTemp < minTemperature; } else return isFrozen; } /******************************************************************************/ /** * Erzeugt eine Permutation der Zahlen von 0 bis length * * @param length Count and highest value of the generated sequence. * @return sequence of numbers, contains every number a single time. The * sequence consists of numbers between 0 and length. */ private int[] createPermutation(int length){ int[] permutation = new int[length]; for( int i = 0; i < permutation.length; i++ ){ int newValue = (int)(Math.random()*length); for( int j = 0; j < i; j++ ) if( newValue == permutation[j] ){ newValue = (int)(Math.random()*length); j = -1; // wird auf 0 zurÔøΩckgesetzt } permutation[i] = newValue; } return permutation; } /******************************************************************************/ /** * Creates a random Vector, with a given length and a random direction. * * @param length Length of the Vector created by this method * @return Vector represented by a Point2D.Double */ private Point2D.Double getRandomVector(double length){ double alpha = Math.random()*Math.PI*2; // double length = Math.random()*maxLength; return new Point2D.Double(length*Math.cos(alpha), length*Math.sin(alpha)); } /******************************************************************************/ /** * Calculates the barycenter of a graph, given by a list. This calculation is * done by summing the coordinates and dividing them with the number of * coordinates. * * @param list List of CellView's * @return Position of the barycenter */ private Point2D.Double computeBarycenter(ArrayList list){ double sumX = 0.0; double sumY = 0.0; for( int i = 0; i < list.size(); i++ ){ CellView view = (CellView) list.get(i); initPosition(view); Point2D.Double pos = getPosition(view); sumX += pos.x; sumY += pos.y; } return new Point2D.Double(sumX/(list.size()), sumY/(list.size())); } /******************************************************************************/ /** * Initialilzes the position of a CellView to the center point of the bounds * of the cell. This initialization is only be done, when the cell isn't * initialised before. * * @param view Cell, the position should be initialized. */ private void initPosition(CellView view){ if( !view.getAttributes().containsKey(KEY_POSITION) ) view.getAttributes().put(KEY_POSITION,new Point2D.Double( view.getBounds().getCenterX(), view.getBounds().getCenterY())); } /******************************************************************************/ /** * Moves the graph to the upper left corner of the drawing space. This is done, * after a successfull run of the algorithm, to correct it's output. */ private void correctCoordinates(){ Rectangle boundingBox = getBoundingBox(); if( boundingBox != null ){ for( int i = 0; i < cellList.size(); i++ ){ CellView view = (CellView) cellList.get(i); Point2D.Double pos = getPosition(view); Point2D.Double topLeftCorner = new Point2D.Double (pos.x + (view.getBounds().getWidth())/2, pos.y + (view.getBounds().getHeight())/2); Point2D.Double newPos = new Point2D.Double( topLeftCorner.x-boundingBox.getX(), topLeftCorner.y-boundingBox.getY()); view.getAttributes().put(KEY_POSITION,newPos); } } } /******************************************************************************/ /** * Computes the bounding box of the whole graph. The result is a Rectangle, * parallel to the X- and Y-axises of the drawing system, closing about the * whole graph. * @return Rectangle, that contains the whole graph. * @see #getBoundingBox(ArrayList) */ private Rectangle getBoundingBox(){ return getBoundingBox(cellList); } /******************************************************************************/ /** * Computes the bounding box of the graph in the given list of CellViews. * The result is a Rectangle, parallel to the X- and Y-axises of the drawing * system, closing about the graph in the given list. * * @param verticeList List containing the CellViews, the bounding box is of * interest. * @return Rectangle, that contains the whole graph, linked in the given list. */ private Rectangle getBoundingBox(ArrayList verticeList){ if( verticeList.size() > 0 ){ Point2D.Double vertexPos = getPosition(0,verticeList); Rectangle2D vertexSize = ((CellView)verticeList.get(0)).getBounds(); double minX = vertexPos.getX(); double minY = vertexPos.getY(); double maxX = vertexPos.getX()+vertexSize.getWidth(); double maxY = vertexPos.getY()+vertexSize.getHeight(); for( int i = 1; i < verticeList.size(); i++ ){ vertexPos = getPosition(i,verticeList); vertexSize =((CellView)verticeList.get(i)).getBounds(); if( minX > vertexPos.getX() ) minX = vertexPos.getX(); if( minY > vertexPos.getY() ) minY = vertexPos.getY(); if( maxX < vertexPos.getX()+vertexSize.getWidth() ) maxX = vertexPos.getX()+vertexSize.getWidth(); if( maxY < vertexPos.getY()+vertexSize.getHeight() ) maxY = vertexPos.getY()+vertexSize.getHeight(); } Rectangle boundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX-minX), (int)(maxY-minY)); return boundingBox; } return null; } /******************************************************************************/ /** * Returns the Position of a Cell contained in {@link #applyCellList}. * * @param index Identifies the cell. This is the index of the cell in * the given list of CellViews * @param list List containing only CellViews * @see #getAttribute(int,String,ArrayList) */ private Point2D.Double getPosition(int index, ArrayList list){ return (Point2D.Double) getAttribute(index,KEY_POSITION,list); } /******************************************************************************/ /** * Returns the temperature of a cell contained in a given list. * * @param index Identifies the cell. This is the index of the cell in * a given list of CellViews * @param list List containing only CellViews * @see #getAttribute(int,String,ArrayList) */ private double getTemperature(int index, ArrayList list){ Double temperature = (Double) getAttribute(index,KEY_TEMPERATURE,list); return temperature.doubleValue(); } /******************************************************************************/ /** * Returns the Position of a Cell. * * @param cell The cell, that holds the position of interest. */ private Point2D.Double getPosition(CellView cell){ return (Point2D.Double) cell.getAttributes().get(KEY_POSITION); } /******************************************************************************/ /** * Returns an attribute from a cell contained in a given list of CellViews. * * @param index Identifies the cell. This is the index of the cell in * the given list of CellViews * @param key Identifies the Attribute, that should be retrieved. * @param list List containing only CellViews */ private Object getAttribute(int index, String key, ArrayList list){ CellView view = (CellView) list.get(index); return view.getAttributes().get(key); } /******************************************************************************/ /** * Arranges the initial cositions for the inserted cells. This is done, by * placing the inserted cells in the barycenter of their relatives. * (possible with errors ... might be fixed soon) */ private void arrangePlacement(CellView[] views){ for( int i = 0; i < cellList.size(); i++ ) initPosition((CellView)cellList.get(i)); if( views != null ){ if( views.length > 0 ){ ArrayList cellLevelList = new ArrayList(); for( int i = 0; i < views.length; i++ ){ if( views[i] instanceof VertexView ){ ArrayList relatives = getRelativesFrom(cellList, views[i]); if( relatives.size() > 0 ){ if( views[i].getAttributes() == null ) views[i].changeAttributes(new AttributeMap()); views[i].getAttributes().put(KEY_POSITION, computeBarycenter(relatives)); cellLevelList.add(views[i]); } } } for( int i = 0; i < cellLevelList.size(); i++ ) cellList.add(cellLevelList.get(i)); int childViewCount = 0; CellView[] possibleChildViews = new CellView[views.length-cellLevelList.size()]; for( int i = 0; i < views.length; i++ ) if( !cellLevelList.contains(views[i]) ) possibleChildViews[childViewCount++] = views[i]; arrangePlacement(possibleChildViews); } } } /******************************************************************************/ /** * Method for the process of layout update. Adds inserted cells to * {@link #applyCellList} and some of their neighbors. Adding of neighbors is * deceided by {@link #layoutUpdateMethod}. If a method is choosen with * perimeter, than the inserted cells are counted, that have a position whithin * the basic radius around inserted cells. then a new radius is calculated by * multiplying the increasial radius with the number of inserted cells found and * adding it to the basic radius. Then all cells, previously layouted whithin * this radius are also added to {@link #applyCellList}. After this, cells * within a given pathlength smaller than {@link #recursionDepth} are added * to {@link #applyCellList} too. * * @param vertexList List of all inserted Vertices. */ public void addApplyableVertices(VertexView[] vertexList){ for( int i = 0; i < vertexList.length; i++ ){ if( !applyCellList.contains(vertexList[i]) ) applyCellList.add(vertexList[i]); if( !cellList.contains(vertexList[i]) ) cellList.add(vertexList[i]); } if( GEMLayoutSettings.KEY_LAYOUT_UPDATE_METHOD_PERIMETERS.equals(layoutUpdateMethod) ){ for( int i = 0; i < vertexList.length; i++ ){ double perimeterSize = perimeterInitSize; Point2D.Double pos = getPosition(vertexList[i]); for( int j = 0; j < vertexList.length; j++ ){ if( i != j ){ Point2D.Double oPos = getPosition(vertexList[j]); if( Math.abs(pos.distance(oPos)) < (perimeterInitSize / 2.0) ) perimeterSize += perimeterSizeInc; } } for( int j = 0; j < cellList.size(); j++ ){ Point2D.Double uPos = getPosition(j,cellList); if( Math.abs(pos.distance(uPos)) < (perimeterSize / 2.0) && !applyCellList.contains(cellList.get(j)) ) applyCellList.add(cellList.get(j)); } } vertexList = new VertexView[applyCellList.size()]; for( int i = 0; i < applyCellList.size(); i++ ) vertexList[i] = (VertexView) applyCellList.get(i); } if( recursionDepth > 0 ) addRelativesToList(vertexList,recursionDepth); } /******************************************************************************/ /** * Recursiv method for adding all relatives whithin a given pathlength away * from the given array of Vertices to {@link #applyCellList}. * * @param vertexList List of Vertices, which relatives might be added. * @param depth pathlength, the vertices adding to {@link #applyCellList} could * be away from the given array's vertices. */ private void addRelativesToList(VertexView[] vertexList, int depth){ if( vertexList == null ) return; if( vertexList.length == 0 ) return; if( depth == 0 ) return; for( int i = 0; i < vertexList.length; i++ ){ ArrayList relatives = getRelatives(vertexList[i]); VertexView[] relativeList = new VertexView[relatives.size()]; for( int j = 0; j < relatives.size(); j++ ){ if( !applyCellList.contains(relatives.get(j)) ) applyCellList.add(relatives.get(j)); if( !cellList.contains(relatives.get(j)) ) cellList.add(relatives.get(j)); relativeList[j] = (VertexView) relatives.get(j); } addRelativesToList(relativeList,depth-1); } } /******************************************************************************/ /** * Will be called, when cells are inserted or removed. When cells are removed, * they are also removed from {@link #cellList}, {@link #applyCellList} and * {@link #edgeList}. If cells are inserted a new layout update process starts. */ public void graphChanged(GraphModelEvent e){ if( !isRunning && isActive ){ isRunning = true; GraphModelEvent.GraphModelChange change = e.getChange(); Object[] objRem = change.getRemoved(); Object[] objIns = change.getInserted(); if( objRem == null && objIns != null ){ // nodes inserted for( int i = 0; i < cellList.size(); i++ ) initPosition((CellView)cellList.get(i)); CellView[] viewInserted = jgraph.getGraphLayoutCache().getMapping(objIns,false); applyCellList = new ArrayList(); /*extracting vertices into []*/ int vertexViewCount = 0; for( int i = 0; i < viewInserted.length; i++ ) if( viewInserted[i] instanceof VertexView ) vertexViewCount++; VertexView[] vertexList = new VertexView[vertexViewCount]; vertexViewCount = 0; for( int i = 0; i < viewInserted.length; i++ ) if( viewInserted[i] instanceof VertexView ) vertexList[vertexViewCount++] = (VertexView) viewInserted[i]; /*extracting vertices into [] done*/ //stops inserting process, if no vertex was inserted if( vertexList.length == 0 ){ isRunning = false; return; } //initialising runtime values from config loadRuntimeValues(VALUES_INC); //the number of cells in applyCellList will probably change sigmaRot /= 1.0 / applyCellList.size(); //positioning the new nodes in the barycenter of old relatives arrangePlacement(vertexList); //add new vertices and some relatives to applyCellList addApplyableVertices(vertexList); if( applyCellList.size() == 0 ){ isRunning = false; return; } // showCellList(applyCellList,Color.GREEN); if( isClusteringEnabled ){ clusterGraph(); } //the number of cells in applyCellList has changed probably sigmaRot *= 1.0 / applyCellList.size(); maxRounds = applyCellList.size() * 4; // performing algorithm on all nodes in applyCellList initialize(); calculate(); // algorithm done if( isClusteringEnabled ) declusterGraph(); if( useOptimizeAlgorithm ) optimizationAlgorithm.run( jgraph, jgraph.getRoots(), null); //moves graph to the upper left corner correctCoordinates(); //taking changes setNewCoordinates(jgraph); //removing algorithms attributes from nodes removeTemporaryLayoutDataFromCells(); } else if( objRem != null && objIns == null ){ // nodes removed CellView[] viewRemoved = jgraph.getGraphLayoutCache().getMapping(objRem,false); for( int i = 0; i < viewRemoved.length; i++ ){ if( viewRemoved[i] instanceof VertexView && cellList.contains(viewRemoved[i]) ){ applyCellList.remove(viewRemoved[i]); cellList.remove(viewRemoved[i]); } } } isRunning = false; } } /******************************************************************************/ /******************** CLUSTERING METHODS **************************************/ /******************************************************************************/ /** * Clusters a graph. Cells, contained in {@link #cellList} and not contained * in {@link #applyCellList} are clustered by this short algorithm. The * algorithm first tries to identify how many cells it should cluster. This * is calculated by subtracting the size of {@link #applyCellList} from * the size of {@link #cellList} and dividing the result by the * {@link #clusteringFactor}. In the next step, the identified number of * clusters are created, and their position is initialised by random. Then * every clusterable cell is added to the cluster where the distance of the * vertex and the cluster is minimal. After adding a cell, the clusters position * is recalculated. Finishing this step, the algorithm tries to minimize the * number of clusters, by sorting the clustered vertices, if there is another * cluster, that distance is shorter than the distance to the cluster, the * vertice is actually in. This can happen, because by moving vertices into the * clusters, the position of the clusters are changed. The minimization runs * until no vertice can be moved anymore. empty clusters are removed and finaly * the clusters are added to {@link #applyCellList}, because they should move * while the upcoming next calculations. That move can later be retrieved by * subtracting the attributes {@link #KEY_POSITION} and * {@link #KEY_CLUSTER_INIT_POSITION}. * * @see #declusterGraph() */ protected void clusterGraph(){ //initialisation int maxClusters = (int)((cellList.size() - applyCellList.size()) / clusteringFactor ); if( maxClusters == 0 ){ System.out.println("maxClusters = 0"); return; } if( cellList.size() <= 1 ){ System.out.println("cellList.size() <= 1"); return; } ArrayList clusterList = new ArrayList(); ArrayList cellsToCluster = new ArrayList(); //identifying all cells, that are clusterable for( int i = 0; i < cellList.size(); i++ ) if( !applyCellList.contains(cellList.get(i)) ) cellsToCluster.add(cellList.get(i)); //initialize clusters VertexView[] clusters = new VertexView[maxClusters]; Rectangle boundingBox = getBoundingBox(); for( int i = 0; i < clusters.length; i++ ){ clusters[i] = new VertexView(null); Map attributes = clusters[i].getAttributes(); attributes.put(KEY_IS_CLUSTER,"true"); attributes.put(KEY_POSITION,new Point2D.Double( Math.random()*boundingBox.width, Math.random()*boundingBox.height)); clusterList.add(clusters[i]); } //cluster all available cells for( int i = 0; i < cellsToCluster.size(); i++ ){ VertexView cell = (VertexView) cellsToCluster.get(i); Point2D.Double cellPos = getPosition(cell); int clusterID = 0; Point2D.Double clusterPos = getPosition((CellView)clusterList.get(0)); double minDistance = MathExtensions.getEuclideanDistance(cellPos,clusterPos); //search for nearest cluster for( int j = 1; j < clusterList.size(); j++ ){ clusterPos = getPosition(j,clusterList); double distance = MathExtensions.getEuclideanDistance(cellPos,clusterPos); if( minDistance > distance ){ minDistance = distance; clusterID = j; } } VertexView cluster = (VertexView) clusterList.get(clusterID); moveVerticeToCluster(cell,cluster); } //initialization done //sorting the clustered vertices. if a vertice is nearer to a clusters //barycenter then to it's own clusters barycenter the vertice is moved //to that cluster. The coordinates of both clusters are recalculated. //this is done, until nothing could be done better. boolean couldMakeItBetter = false; do { couldMakeItBetter = false; for( int i = 0; i < cellsToCluster.size(); i++ ){ VertexView cell = (VertexView) cellsToCluster.get(i); VertexView oldCluster = (VertexView) cell.getAttributes().get(KEY_CLUSTER); Point2D.Double cellPos = getPosition(cell); Point2D.Double clusterPos = getPosition(oldCluster); double distance = MathExtensions.getEuclideanDistance(cellPos,clusterPos); for( int j = 0; j < clusterList.size(); j++ ){ VertexView cluster = (VertexView) clusterList.get(j); if( cluster != oldCluster ){ clusterPos = getPosition(cluster); double newDistance = MathExtensions.getEuclideanDistance(cellPos,clusterPos); if( newDistance < distance ){ moveVerticeToCluster(cell,cluster); couldMakeItBetter = true; break; } } } } } while( couldMakeItBetter ); //empty clusters are removed for( int i = 0; i < clusterList.size(); i++ ){ if( !((VertexView)clusterList.get(i)).getAttributes().containsKey(KEY_CLUSTERED_VERTICES)){ clusterList.remove(i--); } else if( ((ArrayList)((VertexView)clusterList.get(i)).getAttributes().get(KEY_CLUSTERED_VERTICES)).size() == 0 ){ clusterList.remove(i--); } } //remove clustered vertices from cellList for( int i = 0; i < cellsToCluster.size(); i++ ) cellList.remove(cellsToCluster.get(i)); //adding clusters to applyCellList and cellList for( int i = 0; i < clusterList.size(); i++ ){ applyCellList.add(clusterList.get(i)); cellList.add(clusterList.get(i)); } //storing a copy of position, to move vertices while declustering for( int i = 0; i < clusterList.size(); i++ ){ VertexView cluster = (VertexView) clusterList.get(i); Map attribs = cluster.getAttributes(); Point2D.Double clusterPos = (Point2D.Double) attribs.get(KEY_POSITION); attribs.put(KEY_CLUSTER_INIT_POSITION, new Point2D.Double( clusterPos.x, clusterPos.y)); } for( int i = 0; i < clusterList.size(); i++ ){ VertexView cluster = (VertexView)clusterList.get(i); cluster.setCachedBounds(getBoundingBox((ArrayList)cluster.getAttributes().get(KEY_CLUSTERED_VERTICES))); } colorizeClusters(clusterList); stop(20); } /******************************************************************************/ /** * Moves a vertice from the cluster, it is holded, to another cluster. This * implies that the vertice is removed from the old cluster and added to the * new. After this, the positions of the old and the new cluster are * recalculated. * * @param vertice Vertex that should be moved * @param cluster Cluster the vertex should be moved */ protected void moveVerticeToCluster(VertexView vertice, VertexView cluster){ //adding vertice to new cluster if( !cluster.getAttributes().containsKey(KEY_CLUSTERED_VERTICES) ) cluster.getAttributes().put(KEY_CLUSTERED_VERTICES,new ArrayList()); ArrayList clusteredVertices = (ArrayList) cluster.getAttributes().get(KEY_CLUSTERED_VERTICES); clusteredVertices.add(vertice); //removing vertice from old cluster if( vertice.getAttributes().containsKey(KEY_CLUSTER) ){ VertexView oldCluster = (VertexView) vertice.getAttributes().get(KEY_CLUSTER); ArrayList list = (ArrayList)oldCluster.getAttributes().get(KEY_CLUSTERED_VERTICES); list.remove(vertice); computeClusterPosition(oldCluster); } //register cluster in vertice vertice.getAttributes().put(KEY_CLUSTER,cluster); //reposition cluster computeClusterPosition(cluster); } /******************************************************************************/ /** * Recalculates the position of a cluster. The position of a cluster is defined * by the barycenter of the clustered vertices. * * @param cluster Cell, that has to be a cluster, should be repositioned. */ protected void computeClusterPosition(VertexView cluster){ ArrayList clusteredVertices = (ArrayList)cluster.getAttributes().get(KEY_CLUSTERED_VERTICES); Point2D.Double clusterPos = computeBarycenter(clusteredVertices); cluster.getAttributes().put(KEY_POSITION,clusterPos); } /******************************************************************************/ /** * Moves all clusters from {@link #cellList} and {@link #applyCellList}, * extracts their clustered vertices and adds them to {@link #cellList}. While * doing this, it repositions the clustered vertices with the move, the cluster * has made during the calculation. * * @see #clusterGraph() */ protected void declusterGraph(){ if( cellList.size() <= 1 ) return; //first collecting all clusters from applyCellList ArrayList clusterList = new ArrayList(); for( int i = 0; i < cellList.size(); i++ ){ VertexView cell = ((VertexView)cellList.get(i)); if( isCluster(cell) ) clusterList.add(cell); } if( clusterList.size() == 0 ) return; //cleaning up the cell lists for( int i = 0; i < clusterList.size(); i++ ){ cellList.remove(clusterList.get(i)); applyCellList.remove(clusterList.get(i)); } //repositioning and extracting vertices to cellList for( int i = 0; i < clusterList.size(); i++ ){ VertexView cluster = (VertexView)clusterList.get(i); Map attribs = cluster.getAttributes(); Point2D.Double newClusterPos = getPosition(cluster); Point2D.Double oldClusterPos = (Point2D.Double) attribs.get(KEY_CLUSTER_INIT_POSITION); //calculating move, cluster has made during his existance Point2D.Double move = new Point2D.Double(newClusterPos.x - oldClusterPos.x, newClusterPos.y - oldClusterPos.y); ArrayList vertexList = (ArrayList)attribs.get(KEY_CLUSTERED_VERTICES); //applying move to clustered vertices for( int j = 0; j < vertexList.size(); j++ ){ VertexView cell = (VertexView) vertexList.get(j); Point2D.Double cellPos = getPosition(cell); Point2D.Double newCellPos = new Point2D.Double(cellPos.x + move.x, cellPos.y + move.y); cell.getAttributes().put(KEY_POSITION,newCellPos); //refilling clustered vertices in cellList cellList.add(cell); } } } /******************************************************************************/ /** * Returns <code><b>true</b></code> when a cell is a cluster, else * <code<b>false</b></code>. A cell is a cluster when it has under it's * attributes a attribute with the boolean value <code><b>true</b></code> under * the key {@link #KEY_IS_CLUSTER}. * * @param cell cell, that should be researched wheather it is a cluster or not. * @return <code><b>true</b></code> if cell is a cluster, else * <code><b>false</b></code>. */ protected boolean isCluster(CellView cell){ if( cell.getAttributes().containsKey(KEY_IS_CLUSTER)){ if( isTrue((String)cell.getAttributes().get(KEY_IS_CLUSTER))){ return true; } else { System.err.println("FATAL ERROR: CELL CANNOT CLEARLY BE IDENTIFIED AS A CLUSTER!!!"); return false; } } else return false; } /******************************************************************************/ private void colorizeClusters(ArrayList clusterList){ Color[] colorList = new Color[] { Color.black, Color.magenta, Color.yellow, Color.blue, Color.green, Color.gray, Color.cyan, Color.red, Color.darkGray, Color.lightGray, Color.orange, Color.pink }; for( int i = 0; i < clusterList.size(); i++ ) if( i < colorList.length ){ ArrayList clusteredVertices = (ArrayList) ((CellView)clusterList.get(i)).getAttributes().get(KEY_CLUSTERED_VERTICES); showCellList(clusteredVertices,colorList[i]); } } /******************************************************************************/ private void showCellList(ArrayList list, Color color){ Map viewMap = new Hashtable(); for( int i = 0; i < list.size(); i++ ){ CellView view = (CellView)list.get(i); Point2D.Double pos = getPosition(i,list); Rectangle2D r = view.getBounds(); r.setFrame(pos.getX() - r.getWidth() /2.0, pos.getY() - r.getHeight()/2.0, r.getWidth(), r.getHeight()); Object cell = view.getCell(); Map attributes = new Hashtable(); GraphConstants.setBackground(attributes, color); GraphConstants.setBounds (attributes, r); viewMap.put(cell, attributes); } jgraph.getGraphLayoutCache().edit(viewMap,null,null,null); } /******************************************************************************/ private synchronized void stop(double sec){ try{ wait((long)(sec * 1000.0)); } catch( Exception e ){ e.printStackTrace(); } } /******************************************************************************/ private void stop(int sec){ stop((double)sec); } /** * @author winkler * * To change this generated comment edit the template variable "typecomment": * Window>Preferences>Java>Templates. * To enable and disable the creation of type comments go to * Window>Preferences>Java>Code Generation. */ /** * @return Returns the optimizationAlgorithm. */ public AnnealingLayoutAlgorithm getOptimizationAlgorithm() { return optimizationAlgorithm; } /** * @return Returns the config. */ public Properties getConfig() { return config; } public void setConfig(Properties configuration) { this.config = configuration; loadRuntimeValues(VALUES_PUR); } }

The table below shows all metrics for GEMLayoutAlgorithm.java.

MetricValueDescription
BLOCKS146.00Number of blocks
BLOCK_COMMENT80.00Number of block comment lines
COMMENTS785.00Comment lines
COMMENT_DENSITY 0.94Comment density
COMPARISONS223.00Number of comparison operators
CYCLOMATIC219.00Cyclomatic complexity
DECL_COMMENTS146.00Comments in declarations
DOC_COMMENT621.00Number of javadoc comment lines
ELOC837.00Effective lines of code
EXEC_COMMENTS78.00Comments in executable code
EXITS120.00Procedure exits
FUNCTIONS50.00Number of function declarations
HALSTEAD_DIFFICULTY154.09Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY148.00Interface complexity
JAVA0001 0.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 0.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 0.00JAVA0007 Should not declare public field
JAVA0008 0.00JAVA0008 Empty catch block
JAVA0009 0.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 0.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA0020 0.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 0.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 0.00JAVA0029 Private method not used
JAVA0030 0.00JAVA0030 Private field not used
JAVA0031 0.00JAVA0031 Case statement not properly closed
JAVA0032 0.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA003444.00JAVA0034 Missing braces in if statement
JAVA003522.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 0.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 0.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA0049 2.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 0.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 0.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 0.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 0.00JAVA0067 Array descriptor on identifier name
JAVA006814.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 1.00JAVA0075 Method parameter hides field
JAVA0076 0.00JAVA0076 Use of magic number
JAVA0077 0.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 0.00JAVA0081 Boolean literal in comparison
JAVA0082 0.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 0.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 0.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field
JAVA0095 0.00JAVA0095 Uninitialized private field
JAVA0096 0.00JAVA0096 Field in nested class hides outer field
JAVA0098 0.00JAVA0098 Minimize use of implicit field initializers
JAVA0100 1.00JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0101 0.00JAVA0101 Unnecessary modifier for field in interface
JAVA0102 0.00JAVA0102 Last statement in finalize() not super.finalize()
JAVA0103