gt:         attributes = new Hashtable();
context: /**
   * Copyright 2004
   * 
   * This file is part of Peertrust.
   * 
   * Peertrust 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 of the License, or
   * (at your option) any later version.
   * 
   * Peertrust 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 Peertrust; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  package org.peertrust.tnviz.app;
  
  import java.awt.Color;
  import java.awt.Rectangle;
  import java.util.Hashtable;
  import java.util.Map;
  import java.util.Vector;
  import java.util.Enumeration;
  
  import org.jgraph.JGraph;
  import org.jgraph.graph.ConnectionSet;
  import org.jgraph.graph.DefaultCellViewFactory;
  import org.jgraph.graph.DefaultGraphModel;
  import org.jgraph.graph.DefaultPort;
  import org.jgraph.graph.GraphConstants;
  import org.jgraph.graph.GraphLayoutCache;
  import org.jgraph.graph.GraphModel;
  
  import org.peertrust.net.Answer;
  import org.peertrust.net.Query;
  
  /**
   * <p>
   * The TNSeqDiagramm class provides the means to create and maintain a
   * sequence diagramm. It is being used by the TNGraphics class in order to
   * visualize the sequence of queries and answers.
   * </p><p>
   * $Id: TNSeqDiagramm.java,v 1.9 2005/08/07 12:06:52 dolmedilla Exp $
   * <br/>
   * Date: 10-Feb-2005
   * <br/>
   * Last changed: $Date: 2005/08/07 12:06:52 $
   * by $Author: dolmedilla $
   * </p>
   * @author Michael Schaefer and Sebastian Wittler
   */
  public class TNSeqDiagramm {
  
      private JGraph graph;
      private GraphModel model;
      private Hashtable graphElements;
      private final int NODE_HEIGHT = 40;
      private final int SEQ_START_DISTANCE_X = 30;
      private final int SEQ_START_DISTANCE_Y = 50;
      private final int SEQ_DISTANCE_Y = 40;
      private int SEQ_DISTANCE_X = 30;
      private int lastY;
      private int lastX;
      private Graphics graphics;
      private Vector graphPath;
      private Vector nodes; // A vector with the main nodes in the order in which they are created.
      private Hashtable nodesInvisible; // A hashtable with the nodes as key and a vector with their invisible children.
  
      /**
       * The constructor of this class. It requires the graphics object with which
       * this diagramm will be used.
       * @param graphics The graphics object.
       */
      public TNSeqDiagramm(Graphics graphics) {
          graph = new JGraph();
          model = new DefaultGraphModel();
          graphElements = new Hashtable();
          lastY = SEQ_START_DISTANCE_Y;
          lastX = SEQ_START_DISTANCE_X;
          graphPath = new Vector();
          nodes = new Vector();
          nodesInvisible = new Hashtable();
          graph.setModel(model);
          graph.setGraphLayoutCache(new GraphLayoutCache(model,
                  new DefaultCellViewFactory(), true));
          this.graphics = graphics;
      }
  
      /**
       * Cleans the graph by creating a new graph object and by resetting
       * status information like graphPath and graphElements.
       */
      public void wipeGraph() {
          graph = new JGraph();
          model = new DefaultGraphModel();
          graphElements = new Hashtable();
          lastY = SEQ_START_DISTANCE_Y;
          lastX = SEQ_START_DISTANCE_X;
          graphPath = new Vector();
          nodes = new Vector();
          nodesInvisible = new Hashtable();
          graph.setModel(model);
          graph.setGraphLayoutCache(new GraphLayoutCache(model,
                  new DefaultCellViewFactory(), true));
      }
  
      /**
       * Adds a query. The query information is being read out and new nodes and
       * edges are created according to the sequence diagramm structure.
       * @param query The new query.
       */
      public void addQuery(Query query) {
          if (query.getSource() == null || query.getTarget() == null) {
              return;
          }
          String sourceAddress = query.getSource().getAddress();
          String sourceAlias = query.getSource().getAlias();
          int sourcePort = query.getSource().getPort();
          String sourceIdentifier = sourceAlias + ":" + sourceAddress + ":"
                  + sourcePort;
          String targetAddress = query.getTarget().getAddress();
          String targetAlias = query.getTarget().getAlias();
          int targetPort = query.getTarget().getPort();
          String targetIdentifier = targetAlias + ":" + targetAddress + ":"
                  + targetPort;
          String goal = query.getGoal();
          long reqQueryId = query.getReqQueryId();
          TNNode source = null;
          TNNode target = null;
          
          if (graphElements.containsKey("node:" + sourceIdentifier)) {
              source = (TNNode) graphElements.get("node:" + sourceIdentifier);
          }
          else {
              String id = createNode(sourceAlias, sourceAddress, sourceAlias,
                      sourcePort);
              source = getNode(id);
          }
  
          if (graphElements.containsKey("node:" + targetIdentifier)) {
              target = (TNNode) graphElements.get("node:" + targetIdentifier);
          }
          else {
              String id = createNode(targetAlias, targetAddress, targetAlias,
                      targetPort);
              target = getNode(id);
          }
          connectNodes(source, target, goal + " ?", goal, reqQueryId, true,
                  false, -1, "");
      }
  
      /**
       * Adds an answer. The answer information is being read out and new nodes and
       * edges are created according to the sequence diagramm structure.
       * @param answer The new answer.
       */
      public void addAnswer(Answer answer) {
          if (answer.getSource() == null || answer.getTarget() == null) {
              return;
          }
          String sourceAddress = answer.getSource().getAddress();
          String sourceAlias = answer.getSource().getAlias();
          int sourcePort = answer.getSource().getPort();
          String sourceIdentifier = sourceAlias + ":" + sourceAddress + ":"
                  + sourcePort;
          String targetAddress = answer.getTarget().getAddress();
          String targetAlias = answer.getTarget().getAlias();
          int targetPort = answer.getTarget().getPort();
          String targetIdentifier = targetAlias + ":" + targetAddress + ":"
                  + targetPort;
          String goal = answer.getGoal();
          long reqQueryId = answer.getReqQueryId();
          int status = answer.getStatus();
          String proof = "" ;
          if (answer.getProof() != null)
          	proof = answer.getProof().toString();
          TNNode source = null;
          TNNode target = null;
  
          if (graphElements.containsKey("node:" + sourceIdentifier)) {
              source = (TNNode) graphElements.get("node:" + sourceIdentifier);
          }
          else {
              String id = createNode(sourceAlias, sourceAddress, sourceAlias,
                      sourcePort);
              source = getNode(id);
          }
  
          if (graphElements.containsKey("node:" + targetIdentifier)) {
              target = (TNNode) graphElements.get("node:" + targetIdentifier);
          }
          else {
              String id = createNode(targetAlias, targetAddress, targetAlias,
                      targetPort);
              target = getNode(id);
          }
          connectNodes(source, target, goal, goal, reqQueryId, false, true,
                  status, proof);
      }
  
      /**
       * Returns the node with the given id.
       * @param id The id of the node.
       * @return The according node.
       */
      public TNNode getNode(String id) {
          return (TNNode) graphElements.get(id);
      }
  
      /**
       * Creates a new node with the given information.
       * @param object The label object.
       * @param peerAddress The peer address.
       * @param peerAlias The peer alias.
       * @param peerPort The peer port.
       * @return The id of the new node.
       */
      private String createNode(Object object, String peerAddress,
              String peerAlias, int peerPort) {
          TNNode node = new TNNode(object, graph);
          setNodeInformation(node, object.toString(), "node:" + peerAlias + ":"
                  + peerAddress + ":" + peerPort, peerAddress, peerAlias,
                  peerPort);
          graphElements.put(node.getId(), node);
          nodes.add(node);
          Vector elements = new Vector();
          elements.add(node);
          nodesInvisible.put(node, elements);
          DefaultPort port = new DefaultPort();
          node.add(port);
          node.setPort(port);
          Map nodeAttributes = new Hashtable();
          
          if (nodes.size() != 1) {
              lastX = lastX + SEQ_DISTANCE_X;
          }
  
          Rectangle nodeBounds = new Rectangle(lastX, SEQ_START_DISTANCE_Y, node
                  .getLabelWidth(), NODE_HEIGHT);
          node.setX(lastX);
          node.setY(SEQ_START_DISTANCE_Y);
          GraphConstants.setBounds(nodeAttributes, nodeBounds);
          GraphConstants.setMoveable(nodeAttributes, graphics.getNodeMovable());
          GraphConstants.setBendable(nodeAttributes, graphics.getNodeEditable());
          GraphConstants.setSizeable(nodeAttributes, graphics.getNodeEditable());
          GraphConstants.setEditable(nodeAttributes, graphics.getNodeEditable());
          GraphConstants.setBorderColor(nodeAttributes, graphics
                  .getNodeBorderColor());
          GraphConstants.setBackground(nodeAttributes, graphics
                  .getNodeBackgroundColor());
          GraphConstants.setOpaque(nodeAttributes, true);
  
          Map attributes = new Hashtable();
          attributes.put(node, nodeAttributes);
          graph.getGraphLayoutCache().insert(new Object[] { node }, attributes,
                  null, null, null);
  
          lastX += node.getLabelWidth();
  
          return node.getId();
      }
  
      /**
       * Creates an invisible node which is needed to construct the sequence
       * diagramm structure.
       * @return The new invisible node.
       */
      private TNNode createInvisibleNode() {
          TNNode node = new TNNode("", graph);
          DefaultPort port = new DefaultPort();
          node.add(port);
          node.setPort(port);
          node.setInvisible(true);
          Map nodeAttributes = new Hashtable();
          Rectangle nodeBounds = new Rectangle(0, 0, 0, 0);
          
          GraphConstants.setBounds(nodeAttributes, nodeBounds);
          GraphConstants.setMoveable(nodeAttributes, graphics.getNodeMovable());
          GraphConstants.setBendable(nodeAttributes, graphics.getNodeEditable());
          GraphConstants.setSizeable(nodeAttributes, graphics.getNodeEditable());
          GraphConstants.setEditable(nodeAttributes, graphics.getNodeEditable());
          GraphConstants.setBorderColor(nodeAttributes, Color.WHITE);
          GraphConstants.setBackground(nodeAttributes, Color.WHITE);
          GraphConstants.setOpaque(nodeAttributes, true);
  
          Map attributes = new Hashtable();
          attributes.put(node, nodeAttributes);
  
          graph.getGraphLayoutCache().insert(new Object[] { node }, attributes,
                  null, null, null);
  
          return node;
      }
  
      /**
       * Connects the two nodes with the given ids with a new edge with the given information.
       * For this purpose, new invisible nodes and new visible edges are being
       * created to construct the sequence diagramm structure.
       * @param nodeSource The id of the source node.
       * @param nodeTarget The id of the target node.
       * @param object The label object.
       * @param goal The goal.
       * @param reqQueryId The reqQueryId.
       * @param query True, if the connection represents a query, otherwise false.
       * @param answer True, if the connection represents an answer, otherwise false.
       * @param status The status.
       * @param proof The proof.
       * @return The id of the new edge.
       */
      private String connectNodes(String nodeSource, String nodeTarget,
              Object object, String goal, long reqQueryId, boolean query,
              boolean answer, int status, String proof) {
          return connectNodes(getNode(nodeSource), getNode(nodeTarget), object,
                  goal, reqQueryId, query, answer, status, proof);
      }
  
      /**
       * Connects the two given nodes with a new edge with the given information.
       * For this purpose, new invisible nodes and new visible edges are being
       * created to construct the sequence diagramm structure.
       * @param nodeSource The source node.
       * @param nodeTarget The target node.
       * @param object The label object.
       * @param goal The goal.
       * @param reqQueryId The reqQueryId.
       * @param query True, if the connection represents a query, otherwise false.
       * @param answer True, if the connection represents an answer, otherwise false.
       * @param status The status.
       * @param proof The proof.
       * @return The id of the new edge.
       */
      private String connectNodes(TNNode nodeSource, TNNode nodeTarget,
              Object object, String goal, long reqQueryId, boolean query,
              boolean answer, int status, String proof) {
          // Check the length of the edge label, and recompute the distance
          // between the nodes.
          int labelWidth = graph.getFontMetrics(graph.getFont()).stringWidth(
                  object.toString()) + 10;
  
          if ((nodeTarget.getX() > nodeSource.getX())
                  && (nodeTarget.getX() - nodeSource.getX()) < labelWidth) {
              SEQ_DISTANCE_X = labelWidth;
              repositionNodes();
          }
          else if ((nodeSource.getX() > nodeTarget.getX())
                  && (nodeSource.getX() - nodeTarget.getX()) < labelWidth) {
              SEQ_DISTANCE_X = labelWidth;
              repositionNodes();
          }
  
          if (lastY == SEQ_START_DISTANCE_Y) {
              lastY = lastY + SEQ_DISTANCE_Y + NODE_HEIGHT;
          }
          else {
              lastY = lastY + SEQ_DISTANCE_Y;
          }
          
          int newY = lastY;
  
          DefaultPort portSource = (DefaultPort) nodeSource.getPort();
          DefaultPort portTarget = (DefaultPort) nodeTarget.getPort();
          TNNode lastInvisibleNodeSource;
          TNNode lastInvisibleNodeTarget;
          Vector sourceElements = (Vector) nodesInvisible.get(nodeSource);
          lastInvisibleNodeSource = (TNNode) sourceElements.lastElement();
          Vector targetElements = (Vector) nodesInvisible.get(nodeTarget);
          lastInvisibleNodeTarget = (TNNode) targetElements.lastElement();
          TNEdge invisibleEdgeSource = new TNEdge("");
          TNEdge invisibleEdgeTarget = new TNEdge("");
          Map edgeInvisibleSourceAttributes = new Hashtable();
          int arrowInvisible = GraphConstants.ARROW_NONE;
  
          GraphConstants
                  .setLineEnd(edgeInvisibleSourceAttributes, arrowInvisible);
          GraphConstants.setEndFill(edgeInvisibleSourceAttributes, true);
          GraphConstants.setLabelAlongEdge(edgeInvisibleSourceAttributes, false);
          GraphConstants.setMoveable(edgeInvisibleSourceAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setConnectable(edgeInvisibleSourceAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setDisconnectable(edgeInvisibleSourceAttributes,
                  graphics.getEdgeMovable());
          GraphConstants.setBendable(edgeInvisibleSourceAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setSizeable(edgeInvisibleSourceAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setEditable(edgeInvisibleSourceAttributes, graphics
                  .getEdgeEditable());
          GraphConstants.setLineColor(edgeInvisibleSourceAttributes, graphics
                  .getEdgeColor());
  
          Map edgeInvisibleTargetAttributes = new Hashtable();
          GraphConstants
                  .setLineEnd(edgeInvisibleTargetAttributes, arrowInvisible);
          GraphConstants.setEndFill(edgeInvisibleTargetAttributes, true);
          GraphConstants.setLabelAlongEdge(edgeInvisibleTargetAttributes, false);
          GraphConstants.setMoveable(edgeInvisibleTargetAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setConnectable(edgeInvisibleTargetAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setDisconnectable(edgeInvisibleTargetAttributes,
                  graphics.getEdgeMovable());
          GraphConstants.setBendable(edgeInvisibleTargetAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setSizeable(edgeInvisibleTargetAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setEditable(edgeInvisibleTargetAttributes, graphics
                  .getEdgeEditable());
          GraphConstants.setLineColor(edgeInvisibleTargetAttributes, graphics
                  .getEdgeColor());
  
          TNEdge edge = new TNEdge(object);
          edge.setLabel(object.toString());
          Map edgeAttributes = new Hashtable();
          int arrow;
  
          if (query) {
              arrow = GraphConstants.ARROW_SIMPLE;
          }
          else {
              arrow = GraphConstants.ARROW_CLASSIC;
          }
  
          GraphConstants.setLineEnd(edgeAttributes, arrow);
          GraphConstants.setEndFill(edgeAttributes, true);
          GraphConstants.setLabelAlongEdge(edgeAttributes, true);
          GraphConstants.setDashPattern(edgeAttributes, new float[] { 6 });
          GraphConstants.setMoveable(edgeAttributes, graphics.getEdgeMovable());
          GraphConstants
                  .setConnectable(edgeAttributes, graphics.getEdgeMovable());
          GraphConstants.setDisconnectable(edgeAttributes, graphics
                  .getEdgeMovable());
          GraphConstants.setBendable(edgeAttributes, graphics.getEdgeMovable());
          GraphConstants.setSizeable(edgeAttributes, graphics.getEdgeMovable());
          GraphConstants.setEditable(edgeAttributes, graphics.getEdgeEditable());
          GraphConstants.setLineColor(edgeAttributes, graphics.getEdgeColor());
  
          Map attributes = new Hashtable();
          attributes.put(invisibleEdgeSource, edgeInvisibleSourceAttributes);
          ConnectionSet cs = new ConnectionSet();
          TNNode invisibleNodeSource = createInvisibleNode();
          cs.connect(invisibleEdgeSource, lastInvisibleNodeSource.getPort(),
                  invisibleNodeSource.getPort());
          sourceElements.add(invisibleNodeSource);
          nodesInvisible.put(nodeSource, sourceElements);
          //positionInvisibleNode(invisibleNodeSource,nodeSource.getX()+(nodeSource.getLabelWidth()/2),lastY);
          positionInvisibleNode(invisibleNodeSource, nodeSource.getX()
                  + (nodeSource.getLabelWidth() / 2), newY);
          graph.getGraphLayoutCache().insert(
                  new Object[] { invisibleEdgeSource }, attributes, cs, null,
                  null);
  
          attributes = new Hashtable();
          attributes.put(invisibleEdgeTarget, edgeInvisibleTargetAttributes);
          cs = new ConnectionSet();
          TNNode invisibleNodeTarget = createInvisibleNode();
          cs.connect(invisibleEdgeTarget, lastInvisibleNodeTarget.getPort(),
                  invisibleNodeTarget.getPort());
          targetElements.add(invisibleNodeTarget);
          nodesInvisible.put(nodeTarget, targetElements);
  
          //positionInvisibleNode(invisibleNodeTarget,nodeTarget.getX()+(nodeTarget.getLabelWidth()/2),lastY);
          positionInvisibleNode(invisibleNodeTarget, nodeTarget.getX()
                  + (nodeTarget.getLabelWidth() / 2), newY);
          graph.getGraphLayoutCache().insert(
                  new Object[] { invisibleEdgeTarget }, attributes, cs, null,
                  null);
  