HashMap<String, UserColor> usercolors   = new HashMap<String, UserColor>();
HashMap<String, Node> nodes             = new HashMap<String, Node>();
HashMap<String, Edge> edges             = new HashMap<String, Edge>();
HashMap<String, NodeShape> nodeshapes   = new HashMap<String, NodeShape>();
HashMap<String, EdgeShape> edgeshapes   = new HashMap<String, EdgeShape>();
HashMap<String, Thumb> thumbs           = new HashMap<String, Thumb>();
HashMap<String, String> connected_edges = new HashMap<String, String>();
HashMap<String, ImageMap> imageMaps     = new HashMap<String, ImageMap>();
ArrayList<Node> vizgroup                = new ArrayList<Node>();
PImage img;
int bgColor = White;
int u_clr; //user color

// ADDED BY IRI TO ACCESS NODES AND EDGES FROM JS

HashMap getNodes() {
  return nodes;
}

HashMap getEdges() {
  return edges;
}

// END ADDITION

////////  SAVE & LOAD NODES, EDGED  ////////////////////////////////////////////
void allSave() {
  String proj, asc_id, a_proj, id, from_proj, name, grp, to_id, to_proj, r_name, r_from, r_to;
  boolean root, fixed;
  int x, y, assoc;
  Node node;
  Edge edge;
  String root_str = "";
  Iterator it = nodes.entrySet().iterator();
  while (it.hasNext ()) {
    Map.Entry e = (Map.Entry)it.next();
    String k  = (String) e.getKey();
    node = (Node) e.getValue();
    if (node!=null) {
      id    = node.id;
      proj  = node.proj;
      name  = node.name;
      grp   = node.grp;
      assoc = node.assoc;
      x     = node.x;
      y     = node.y;
      root  = node.root;
      fixed = node.fixed;
      if (root) 
        root_str = "\"root\":[{ \"id\":\"" + id + "\", \"proj\":\""+ proj + "\", "
		  + "\"name\":\""+ name + "\", \"grp\":\""+ grp + "\", " 
                  + "\"assoc\":" + assoc + ", \"x\":" + x + ", \"y\":" + y +" }]";
    }
  }
  //if (debug) println(root_str);
  String nodes_str = "\"node\":[ ";
  it = nodes.entrySet().iterator();
  while (it.hasNext ()) {
    Map.Entry e = (Map.Entry)it.next();
    String k  = (String) e.getKey();
    node = (Node) e.getValue();
    if (node!=null) {
      id    = node.id;
      proj  = node.proj;
      name  = node.name;
      grp   = node.grp;
      assoc = node.assoc;
      x     = node.x;
      y     = node.y;
      root  = node.root;
      fixed = node.fixed;
      nodes_str += "{ \"id\":\"" + id + "\", \"proj\":\"" + proj + "\", "
		 + "\"name\":\""+ name + "\", \"grp\":\""+ grp + "\", " 
                 + "\"assoc\":" + assoc + ", \"x\":" + x + ", \"y\":" + y +" },";
    }
  }
  //nodes_str += " {} ]";
  nodes_str = nodes_str.substring(0, nodes_str.length()-1) + " ]";
  //if (debug) println(nodes_str);
  String edges_str = "\"edge\":[ ";
  it = edges.entrySet().iterator();
  while (it.hasNext ()) {
    Map.Entry e = (Map.Entry)it.next();
    String k  = (String) e.getKey();
    edge = (Edge) e.getValue();
    asc_id = edge.asc_id;    
    a_proj = edge.proj;    
    id     = edge.from.id;
    from_proj = edge.from.proj;
    to_id  = edge.to.id;
    to_proj = edge.to.proj;
    r_name = edge.r_name;
    r_from = edge.r_from;
    r_to   = edge.r_to;
    edges_str += "{ \"asc_id\":\"" + asc_id + "\", \"a_proj\":\""+ a_proj +"\","
		+ " \"id\":\"" + id + "\", \"from_proj\":\""+ from_proj +"\","
		+ " \"to_id\":\"" + to_id + "\", \"to_proj\":\"" + to_proj + "\"," 
                + " \"r_name\":\"" + r_name +"\", \"r_from\":\"" + r_from + "\", \"r_to\":\"" + r_to + "\" },";
  }
  //edges_str += " {} ]";
  edges_str = edges_str.substring(0, edges_str.length()-1) + " ]";
  if (debug) 
    println("{" + root_str + ", " + nodes_str + ", " + edges_str + "}");
  if (null != javascript) javascript.allbackup("{" + root_str + ", " + nodes_str + ", " + edges_str + "}");
}

void allRetrieve() {
  physics.clear(); 
  nodes.clear();
  edges.clear();
  connected_edges.clear();
  javascript.allretrieve("");
}

int cvalue(int hex) {
  int r = (hex & 0xff0000)>>16;
  int g = (hex & 0xff00)>>8;
  int b = hex & 0xff;
  return r+g+b;
}

int toInt(String s) {
  int i = 0;
  if ("0".equals(s)) i = 0;
  else if ("1".equals(s)) i = 1;
  else if ("2".equals(s)) i = 2;
  else if ("3".equals(s)) i = 3;
  else if ("4".equals(s)) i = 4;
  else if ("5".equals(s)) i = 5;
  else if ("6".equals(s)) i = 6;
  else if ("7".equals(s)) i = 7;
  else if ("8".equals(s)) i = 8;
  else if ("9".equals(s)) i = 9;
  else if ("a".equals(s)) i = 10;
  else if ("A".equals(s)) i = 10;
  else if ("b".equals(s)) i = 11;
  else if ("B".equals(s)) i = 11;
  else if ("c".equals(s)) i = 12;
  else if ("C".equals(s)) i = 12;
  else if ("d".equals(s)) i = 13;
  else if ("D".equals(s)) i = 13;
  else if ("e".equals(s)) i = 14;
  else if ("E".equals(s)) i = 14;
  else if ("f".equals(s)) i = 15;
  else if ("F".equals(s)) i = 15;
  return i;
}

int hex2int(String s) {
  String a = s.substring(0, 1);
  String b = s.substring(1, 2);
  int ret = 16 * toInt(a) + toInt(b);
  //if (debug) println(s + " " + a + " " + b + " " + ret);
  return ret;
}

int str2color(String str) {
  int c;
  int r = hex2int(str.substring(0, 2));
  int g = hex2int(str.substring(2, 4));
  int b = hex2int(str.substring(4, 6));
  //if (debug) println(str + " " + r + " " + g + " " + b);
  c = color(r, g, b);
  return c;
}

void initNode(String id, String name, String grp, String uid, String proj) {
  if (debug) println("initNode(id=" + id + " name=" + name + " grp=" + grp + " uid=" + uid + " proj=" + proj + ")");
  //name = unescapeSTR( name );
  Node n = findNode(id, proj);
  if (n == null) n = new Node(id, name, grp, uid, proj);
  rootNode(n);
}

////////  GROUP SHAPE & COLOR  /////////////////////////////////////////////////
void groupShape() {
  if (nodetrace) print("groupShape");
  javascript.group_shapes();
}

int hexToColor(String clr) {
  String s = clr.replaceFirst("^#", "");
  int c = str2color( s );
  return c;
}

int color2int(String clr) {
  int c = 0;
  if ( "#".equals(clr.substring(0, 1))) {
    c = hexToColor(clr);
  }
  else if ( "AliceBlue".equals(clr) )         c = AliceBlue;
  else if ( "AntiqueWhite".equals(clr) )      c = AntiqueWhite;
  else if ( "Aqua".equals(clr) )              c = Aqua;
  else if ( "Aquamarine".equals(clr) )        c = Aquamarine;
  else if ( "Azure".equals(clr) )             c = Azure;
  else if ( "Beige".equals(clr) )             c = Beige;
  else if ( "Bisque".equals(clr) )            c = Bisque;
  else if ( "Black".equals(clr) )             c = Black;
  else if ( "BlanchedAlmond".equals(clr) )    c = BlanchedAlmond;
  else if ( "Blue".equals(clr) )              c = Blue;
  else if ( "BlueViolet".equals(clr) )        c = BlueViolet;
  else if ( "Brown".equals(clr) )             c = Brown;
  else if ( "BurlyWood".equals(clr) )         c = BurlyWood;
  else if ( "CadetBlue".equals(clr) )         c = CadetBlue;
  else if ( "Chartreuse".equals(clr) )        c = Chartreuse;
  else if ( "Chocolate".equals(clr) )         c = Chocolate;
  else if ( "Coral".equals(clr) )             c =  Coral;
  else if ( "CornflowerBlue".equals(clr) )    c = CornflowerBlue;
  else if ( "Cornsilk".equals(clr) )          c = Cornsilk;
  else if ( "Crimson".equals(clr) )           c = Crimson;
  else if ( "Cyan".equals(clr) )              c = Cyan;
  else if ( "DarkBlue".equals(clr) )          c = DarkBlue;
  else if ( "DarkCyan".equals(clr) )          c = DarkCyan;
  else if ( "DarkGoldenRod".equals(clr) )     c = DarkGoldenRod;
  else if ( "DarkGray".equals(clr) )          c = DarkGray;
  else if ( "DarkGrey".equals(clr) )          c = DarkGrey;
  else if ( "DarkGreen".equals(clr) )         c = DarkGreen;
  else if ( "DarkKhaki".equals(clr) )         c = DarkKhaki;
  else if ( "DarkMagenta".equals(clr) )       c = DarkMagenta;
  else if ( "DarkOliveGreen".equals(clr) )    c = DarkOliveGreen;
  else if ( "Darkorange".equals(clr) )        c = Darkorange;
  else if ( "DarkOrchid".equals(clr) )        c = DarkOrchid;
  else if ( "DarkRed".equals(clr) )           c = DarkRed;
  else if ( "DarkSalmon".equals(clr) )        c = DarkSalmon;
  else if ( "DarkSeaGreen".equals(clr) )      c = DarkSeaGreen;
  else if ( "DarkSlateBlue".equals(clr) )     c = DarkSlateBlue;
  else if ( "DarkSlateGray".equals(clr) )     c = DarkSlateGray;
  else if ( "DarkSlateGrey".equals(clr) )     c = DarkSlateGrey;
  else if ( "DarkTurquoise".equals(clr) )     c = DarkTurquoise;
  else if ( "DarkViolet".equals(clr) )        c = DarkViolet;
  else if ( "DeepPink".equals(clr) )          c = DeepPink;
  else if ( "DeepSkyBlue".equals(clr) )       c = DeepSkyBlue;
  else if ( "DimGray".equals(clr) )           c = DimGray;
  else if ( "DimGrey".equals(clr) )           c = DimGrey;
  else if ( "DodgerBlue".equals(clr) )        c = DodgerBlue;
  else if ( "FireBrick".equals(clr) )         c = FireBrick;
  else if ( "FloralWhite".equals(clr) )       c = FloralWhite;
  else if ( "ForestGreen".equals(clr) )       c = ForestGreen;
  else if ( "Fuchsia".equals(clr) )           c = Fuchsia;
  else if ( "Gainsboro".equals(clr) )         c = Gainsboro;
  else if ( "GhostWhite".equals(clr) )        c = GhostWhite;
  else if ( "Gold".equals(clr) )              c = Gold;
  else if ( "GoldenRod".equals(clr) )         c = GoldenRod;
  else if ( "Gray".equals(clr) )              c = Gray;
  else if ( "Grey".equals(clr) )              c = Grey;
  else if ( "Green".equals(clr) )             c = Green;
  else if ( "GreenYellow".equals(clr) )       c = GreenYellow;
  else if ( "HoneyDew".equals(clr) )          c = HoneyDew;
  else if ( "HotPink".equals(clr) )           c = HotPink;
  else if ( "IndianRed ".equals(clr) )        c = IndianRed ;
  else if ( "Indigo ".equals(clr) )           c = Indigo ;
  else if ( "Ivory".equals(clr) )             c = Ivory;
  else if ( "Khaki".equals(clr) )             c = Khaki;
  else if ( "Lavender".equals(clr) )          c = Lavender;
  else if ( "LavenderBlush".equals(clr) )     c = LavenderBlush;
  else if ( "LawnGreen".equals(clr) )         c = LawnGreen;
  else if ( "LemonChiffon".equals(clr) )      c = LemonChiffon;
  else if ( "LightBlue".equals(clr) )         c = LightBlue;
  else if ( "LightCoral".equals(clr) )        c = LightCoral;
  else if ( "LightCyan".equals(clr) )         c = LightCyan;
  else if ( "LightGoldenRodYellow".equals(clr) ) c = LightGoldenRodYellow;
  else if ( "LightGray".equals(clr) )         c = LightGray;
  else if ( "LightGrey".equals(clr) )         c = LightGrey;
  else if ( "LightGreen".equals(clr) )        c = LightGreen;
  else if ( "LightPink".equals(clr) )         c = LightPink;
  else if ( "LightSalmon".equals(clr) )       c = LightSalmon;
  else if ( "LightSeaGreen".equals(clr) )     c = LightSeaGreen;
  else if ( "LightSkyBlue".equals(clr) )      c = LightSkyBlue;
  else if ( "LightSlateGray".equals(clr) )    c = LightSlateGray;
  else if ( "LightSlateGrey".equals(clr) )    c = LightSlateGrey;
  else if ( "LightSteelBlue".equals(clr) )    c = LightSteelBlue;
  else if ( "LightYellow".equals(clr) )       c = LightYellow;
  else if ( "Lime".equals(clr) )              c = Lime;
  else if ( "LimeGreen".equals(clr) )         c = LimeGreen;
  else if ( "Linen".equals(clr) )             c = Linen;
  else if ( "Magenta".equals(clr) )           c = Magenta;
  else if ( "Maroon".equals(clr) )            c = Maroon;
  else if ( "MediumAquaMarine".equals(clr) )  c = MediumAquaMarine;
  else if ( "MediumBlue".equals(clr) )        c = MediumBlue;
  else if ( "MediumOrchid".equals(clr) )      c = MediumOrchid;
  else if ( "MediumPurple".equals(clr) )      c = MediumPurple;
  else if ( "MediumSeaGreen".equals(clr) )    c = MediumSeaGreen;
  else if ( "MediumSlateBlue".equals(clr) )   c = MediumSlateBlue;
  else if ( "MediumSpringGreen".equals(clr) ) c = MediumSpringGreen;
  else if ( "MediumTurquoise".equals(clr) )   c = MediumTurquoise;
  else if ( "MediumVioletRed".equals(clr) )   c = MediumVioletRed;
  else if ( "MidnightBlue".equals(clr) )      c = MidnightBlue;
  else if ( "MintCream".equals(clr) )         c = MintCream;
  else if ( "MistyRose".equals(clr) )         c = MistyRose;
  else if ( "Moccasin".equals(clr) )          c = Moccasin;
  else if ( "NavajoWhite".equals(clr) )       c = NavajoWhite;
  else if ( "Navy".equals(clr) )              c = Navy;
  else if ( "OldLace".equals(clr) )           c = OldLace;
  else if ( "Olive".equals(clr) )             c = Olive;
  else if ( "OliveDrab".equals(clr) )         c = OliveDrab;
  else if ( "Orange".equals(clr) )            c = Orange;
  else if ( "OrangeRed".equals(clr) )         c = OrangeRed;
  else if ( "Orchid".equals(clr) )            c = Orchid;
  else if ( "PaleGoldenRod".equals(clr) )     c = PaleGoldenRod;
  else if ( "PaleGreen".equals(clr) )         c = PaleGreen;
  else if ( "PaleTurquoise".equals(clr) )     c = PaleTurquoise;
  else if ( "PaleVioletRed".equals(clr) )     c = PaleVioletRed;
  else if ( "PapayaWhip".equals(clr) )        c = PapayaWhip;
  else if ( "PeachPuff".equals(clr) )         c = PeachPuff;
  else if ( "Peru".equals(clr) )              c = Peru;
  else if ( "Pink".equals(clr) )              c = Pink;
  else if ( "Plum".equals(clr) )              c = Plum;
  else if ( "PowderBlue".equals(clr) )        c = PowderBlue;
  else if ( "Purple".equals(clr) )            c = Purple;
  else if ( "Red".equals(clr) )               c = Red;
  else if ( "RosyBrown".equals(clr) )         c = RosyBrown;
  else if ( "RoyalBlue".equals(clr) )         c = RoyalBlue;
  else if ( "SaddleBrown".equals(clr) )       c = SaddleBrown;
  else if ( "Salmon".equals(clr) )            c = Salmon;
  else if ( "SandyBrown".equals(clr) )        c = SandyBrown;
  else if ( "SeaGreen".equals(clr) )          c = SeaGreen;
  else if ( "SeaShell".equals(clr) )          c = SeaShell;
  else if ( "Sienna".equals(clr) )            c = Sienna;
  else if ( "Silver".equals(clr) )            c = Silver;
  else if ( "SkyBlue".equals(clr) )           c = SkyBlue;
  else if ( "SlateBlue".equals(clr) )         c = SlateBlue;
  else if ( "SlateGray".equals(clr) )         c = SlateGray;
  else if ( "SlateGrey".equals(clr) )         c = SlateGrey;
  else if ( "Snow".equals(clr) )              c = Snow;
  else if ( "SpringGreen".equals(clr) )       c = SpringGreen;
  else if ( "SteelBlue".equals(clr) )         c = SteelBlue;
  else if ( "Tan".equals(clr) )               c = Tan;
  else if ( "Teal".equals(clr) )              c = Teal;
  else if ( "Thistle".equals(clr) )           c = Thistle;
  else if ( "Tomato".equals(clr) )            c = Tomato;
  else if ( "Turquoise".equals(clr) )         c = Turquoise;
  else if ( "Violet".equals(clr) )            c = Violet;
  else if ( "Wheat".equals(clr) )             c = Wheat;
  else if ( "White".equals(clr) )             c = White;
  else if ( "WhiteSmoke".equals(clr) )        c = WhiteSmoke;
  else if ( "Yellow".equals(clr) )            c = Yellow;
  else if ( "YellowGreen".equals(clr) )       c = YellowGreen;
  return c;
}

void newUserColor(String uid, int c, String show) {
  if (debug) println("newUserColor(" + uid + "," + c + "," + show + ")");
  boolean s =("show".equals(show))? true:false;
  UserColor usercolor = new UserColor(uid, uid, c, s);
  usercolors.put( uid, usercolor );
  if (javascript != null) javascript.username(uid); 
}

void setUserColor(String uid, String uname) {
  if (debug) println("setUserColor(" + uid + "," + uname + ")");
  UserColor usercolor = (UserColor) usercolors.get(uid);
  if (usercolor != null) {
    usercolor.uid  = uid;
    usercolor.name = uname;
    usercolors.put( uid, usercolor );
  }
}

public class UserColor {
  String  uid;
  String  name;
  int     c;
  boolean show;

  UserColor(String uid, String name, int c, boolean show) {
    this.uid  = uid;
    this.name  = name;
    this.c    = c;
    this.show = show;
  }
}
// NodeShape
void newNodeShape(String grp, String s, String clr) {
  if (debug || nodetrace) println("newNodeshape(" + grp + "," + s + "," + clr + ")");
  int c = color2int(clr);
  NodeShape nodeshape = new NodeShape(grp, s, c);
  addNodeShape(nodeshape);
}

void addNodeShape(NodeShape nodeshape) {
  if (nodetrace) print("addNodeShape("+nodeshape.grp+","+nodeshape.s+","+hex(nodeshape.c,6)+")");
  String grp = nodeshape.grp;
  /*if (nodeshapes.containsKey( grp )) {
    nodeshapes.remove( grp );
  } */
  nodeshapes.put( grp, nodeshape );
}

NodeShape findNodeShape(String grp) {
  NodeShape node_shape = (NodeShape) nodeshapes.get(grp);
  if (nodetrace) {
    if(node_shape==null) 
      println("\tfindNodeShape grp="+grp+" shape undefined.");
    else 
      println("\tfindNodeShape grp="+grp+" shape="+node_shape.s+" color="+hex(node_shape.c,6));
  }
  return node_shape;
}

public class NodeShape {
  String grp;
  String s; 
  int    c;

  NodeShape(String grp, String s, int c) {
    this.grp  = grp;
    this.s    = s;
    this.c    = c;
  }
}

// EdgeShape
void newEdgeShape(String r_name, String s, String clr) {
  if (edgetrace) println("newEdgeShape(" + r_name + "," + s + "," + clr + ")");
  int c = color2int(clr);
  EdgeShape edgeshape = new EdgeShape(r_name, s, c);
  addEdgeShape(edgeshape);
}

void addEdgeShape(EdgeShape edgeshape) {
  if (edgetrace) println("addEdgeShape edgeshape=("+edgeshape.r_name+","+edgeshape.s+","+hex(edgeshape.c,6)+") ");
  String r_name = edgeshape.r_name;
  /*if (edgeshapes.containsKey( r_name )) {
    edgeshapes.remove( r_name );
  } */
  edgeshapes.put( r_name, edgeshape );
}

EdgeShape findEdgeShape(String r_name) {
  if (edgetrace) println("\tfindEdgeShape("+r_name+")");  
  if (r_name==null || r_name.equals("")) return null;
  EdgeShape edgeshape = (EdgeShape) edgeshapes.get(r_name);
  return edgeshape;
}

public class EdgeShape {
  String r_name;
  String s; 
  int    c;

  EdgeShape(String r_name, String s, int c) {
    this.r_name  = r_name;
    this.s    = s;
    this.c    = c;
  }
}

////////  IMAGE MAP MODEL  ///////////////////////////////////////////////
ImageMap newImageMap(String url) {
  //if (debug) println("newImageMap(" + url + ")");
  ImageMap imap = findImageMap(url);
  if (imap == null) {
    imap = new ImageMap(url);
    addImageMap(imap);
  }
  return imap;
}

ImageMap findImageMap(String url) {
  //if (debug) println("\tfindImageMap("+url+")");  
  if (url==null || url.equals("")) return null;
  ImageMap imap = (ImageMap) imageMaps.get(url);
  return imap;
}

void addImageMap(ImageMap imap) {
  //if (debug) println("addImage imap=("+imap.url+") ");
  String url = imap.url;
  /*if (imageMaps.containsKey( url )) {
    imageMaps.remove( url );
  } */
  imageMaps.put( url, imap );
}

class ImageMap {     
    String url;   
    PImage img;
    int w,h;
    
    ImageMap(String url) {
      this.url = url;          
      int siz = 80;
      w = h = 0;
      this.img = loadImage(url);
      if(this.img != null) {
        w = img.width;
        h = img.height;
        if (w > h) {
          h = round(siz * h / w);
          w = siz; 
        } else {
          w = round(siz * w / h); 
          h = siz;
        }
        img.resize(w, h);  
      } 
    }
    
    void show(int x, int y) {
        image(this.img, x - this.w, y - this.h/2);      
    }
}

////////  THUMBNAIL MODEL  ///////////////////////////////////////////////
void newThumb(String id, int start, int end) {
  if (debug) println("newThumb(" + id + "," + start + "," + end + ")");
  Thumb thumb = new Thumb(id, start, end);
  addThumb(thumb);
}

void addThumb(Thumb thumb) {
  if (debug) println("addThumb thumb=("+thumb.start+","+thumb.end+") ");
  String id = thumb.id;
  /*if (thumbs.containsKey( id )) {
    thumbs.remove( id );
  } */
  thumbs.put( id, thumb );
}

Thumb findThumb(String id) {
  //if (debug) println("\tfindThumb("+id+")");  
  if (id==null || id.equals("")) return null;
  Thumb thumb = (Thumb) thumbs.get(id);
  return thumb;
}

class Thumb {     
    String id;   
    String prefix;   
    int start;  
    int end;
    String postfix;
    PImage image;
    
    Thumb(String id, int start, int end) {
      this.id      = id;
      this.prefix  = "thumbs/";
      this.start   = start;
      this.end     = end;
      this.postfix = "_in.jpg";     
    }
}

////////  GRAPH DATA MODEL  ///////////////////////////////////////////////
public class Node {
  Particle p;
  int     x, y;
  int     mass;
  String  id, proj, uid;
  String  name, grp, abst;
  String  url;
  String  shape;
  int     c;
  int     size;
  int     w, h;
  //String  hide;
  boolean checked;
  boolean fixed;
  boolean root;
  String  isa;
  int     count;
  int     assoc;
  String  freeDirection;
  String  vizgroup;

  Node(String _id, String _name, String _grp, String _uid, String _proj) {
    this.id   = _id;
    this.name = _name;
    this.grp  = _grp;
    this.uid  = _uid;
    this.proj = _proj;
    this.size = 18;
    this.mass = 1;// MASS;
    this.x    = Math.round( random(-128, 128) );
    this.y    = Math.round( random(-128, 128) );
    this.p    = physics.makeParticle(this.mass, this.x, this.y /*, 0*/);
    this.checked = false;
    this.fixed = false;
    this.root  = false;
    this.isa   = "";
    this.count = 0;
    this.assoc = 0;
    String s   = "RECTANGLE";    // Default
    int c      = LightSlateGray; // Default
    NodeShape node_shape = findNodeShape( grp );
    if (node_shape != null) {
      s = node_shape.s;
      c = node_shape.c;
    }
    this.shape = s;
    this.c    = c;
    this.w    = 10;
    this.h    = 12;
    if (nodetrace) println( "\tDEFINE Node( " + id + ", " + name + ", " + grp + ", " + uid + ", " + proj +" )" );
  }

  void increment() {
    mass++;
  }

  void setURL(String url) {
    this.url = url;
  }

  void fix() {
    this.fixed = true;
    this.p.makeFixed();
  }

  void free() {
    this.fixed = false;
    this.p.makeFree();
  }

  void position(int x, int y) {
    this.x = x;
    this.y = y;
    this.p.position.x = x;    
    this.p.position.y = y;
  }

  void update() {
    this.x = Math.round( p.position.x );
    this.y = Math.round( p.position.y );
  }

  void move(float mx, float my) {
    this.x += mx;
    this.y += my;
    this.position(this.x, this.y);
    damper = 1.0;
  }

  void affix(int diff, float x, float y, float w, float h, int s) {
    if (diff > 0) {
      if (diff>1000) s *= 2;
      else if (diff>100) s *= 1.5;
      String unopen = "" + diff;
      stroke(White);
      strokeWeight(1);
      //stroke(edgeColor);
      stroke(DeepPink);
      fill(DeepPink);
      rect(x + w/2, y - h/2, s, s);
      fill(White);
      textSize(8);
      text(unopen, x + w/2, y - h/2);
      textSize(12);
    }
  }

  void drawShape(String label, float dispX, float dispY, boolean highlite) {
    float sf = sqrt(mass);
    sf = 1 + (sf-1)/8;       // scale factor of each node
    int tsize = round(10*sf);
    if (tsize > 48) {
      textSize(48);
    } else if (tsize > 12) {
      textSize(tsize);
    } else {
      textSize(12);
    }
    w = round( textWidth(label) + 30 );
    h = round( sf * this.size );
    if (this.root) fill(rootColor);
    if ("".equals(this.shape)) {
      rectMode(CENTER);
      rect(dispX, dispY, w, h);
    }
    else if ("CIRCLE".equals(this.shape)) {
      ellipseMode(CENTER);
      ellipse(dispX, dispY, w, w);
    }
    else if ("ELLIPSE".equals(this.shape)) {
      ellipseMode(CENTER);
      ellipse(dispX, dispY, w + 4, h + 4);
    }
    else if ("ROUNDED".equals(this.shape)) {
      rectMode(CENTER);
      ellipseMode(CENTER);
      roundedRect(dispX, dispY, w, h, h/2);
    }
    else if ("MARK".equals(this.shape)) {
      int radius = 18;
      fill(bgColor, 10); 
      stroke(bgColor, 10);
      rectMode(CENTER);
      rect(dispX + w*sf/2 + radius, dispY, w, h);      
      fill(this.c);
      stroke( (highlite)? selectColor:u_clr );
      ellipseMode(CENTER);
      //ellipse(dispX, dispY, h*sf, h*sf);
      ellipse(dispX, dispY, radius, radius);
    } 
    else {//Default shape is RECTANGLE
      rectMode(CENTER);
      rect(dispX, dispY, w, h);
    }

    int diff = this.assoc - this.count;
    String unopen = "";
    if ( diff > 0 ) unopen += diff;
    textAlign(CENTER, CENTER);
    if ("MARK".equals(this.shape)) {
      fill(Black); 
      text(label, dispX + w*sf/2, dispY);
      affix(diff, dispX - w*sf/2 + h*sf/2, dispY, w*sf, h*sf, 9);
    } 
    else {
      fill(Black);
      if ( cvalue(this.c) < cvalue(#A0A0A0) ) fill(White);  
      text(label, dispX, dispY);
      affix(diff, dispX, dispY, w*sf, h*sf, 9);
    }
    if (url!=null) {
      ImageMap imap = newImageMap(url);
      imap.show(round(dispX - w*sf/2), round(dispY));
    }
  }

  void show() {
    Particle p = this.p;
    update();
    if (p!=null) {
      float dispX = factor * p.position.x;    
      float dispY = factor * p.position.y;
      String s   = "RECTANGLE";    // Default
      int c      = LightSlateGray; // Default
      NodeShape node_shape = findNodeShape( grp );
      if (node_shape != null) {
        s = node_shape.s;
        c = node_shape.c;
      }
      this.shape = s;
      this.c     = c;
      fill(this.c);
      UserColor usercolor = (UserColor) usercolors.get(this.uid);
      strokeWeight(1.5);
      if (usercolor!=null) {
        u_clr = usercolor.c;
        stroke(u_clr);
      }
      else {
        u_clr = color(32*int(random(255)/32), 32*int(random(255)/32), 32*int(random(255)/32));
        while (u_clr == selectColor){
          u_clr = color(32*int(random(255)/32), 32*int(random(255)/32), 32*int(random(255)/32));          
        }
        newUserColor(uid, u_clr, "show");
        stroke(u_clr);        
      }
      //textSize(12);      
      String topicname = unescapeSTR(this.name);
      UserColor uc = (UserColor) usercolors.get(uid);
      String label = (showusername && uc.name!=null)? "["+uc.name+"] "+topicname:topicname;
      if ( label.length() > LABEL_LENGTH ) label = label.substring(0, LABEL_LENGTH) + "...";
      this.drawShape(label, dispX, dispY, false);
    }
  }

  void highlight() {
    Particle p = this.p;
    float dispX=0, dispY=0;
    int seq = 0;
    if (p!=null) {
      dispX = factor * p.position.x;    
      dispY = factor * p.position.y;
      stroke(selectColor);
      strokeWeight(2.0);
      fill(this.c);
      //textSize(12); 
      UserColor uc = (UserColor) usercolors.get(uid);
      String topicname = unescapeSTR(this.name);
      String label = (showusername && uc.name!=null)? "["+uc.name+"] "+topicname:topicname;
      this.drawShape(label, dispX, dispY, true);
      stroke(bgColor);
      strokeWeight(0.1);
      /*Thumb thumb = findThumb(id);
      if (thumb!=null) {
        int num = thumb.end-thumb.start;
        num=(num>0)?num:1;
        String filename = 
  	    	    thumb.prefix + 
  	    	    (thumb.start + (seq++ / 30) % num) +
  	    	    thumb.postfix;
        if (debug) println(filename);
        img = loadImage(filename);
        if (img!=null) image(img, dispX - 16, dispY + 4);
      }
      */
    }
    if (nodetrace) {
      fill(Gray);
      text(isa, dispX - 30, dispY + 12);
      text("assoc="+assoc+" count="+count, dispX - 30, dispY + 24);
      text("fixed="+fixed+" root="+root, dispX - 30, dispY + 36);      
      text(id+"@"+proj+" grp="+grp+" url="+url, dispX - 30, dispY + 48);
    }
  }
}

void roundedRect(float x, float y, float w, float h, float r) {
  pushMatrix();
  translate(x, y);
  beginShape();
  vertex(  w/2 - r, -h/2 );
  bezierVertex( w/2 - r, -h/2, w/2, -h/2, w/2, -h/2 + r);
  vertex(  w/2, h/2 - r );
  bezierVertex( w/2, h/2, w/2 - r, h/2, w/2 - r, h/2);
  vertex( -w/2 + r, h/2 );
  bezierVertex( - w/2, h/2, -w/2, h/2 - r, -w/2, h/2 - r );
  vertex( -w/2, -h/2 + r );
  bezierVertex( -w/2, -h/2, -w/2 + r, -h/2, -w/2 + r, -h/2 );
  endShape(CLOSE);
  popMatrix();
}

public class Edge {
  Spring s;
  Node   from;
  Node   to;
  String from_is;
  String to_is;
  String proj, uid;
  String asc_id;
  String id, to_id, to_name;
  String r_name, r_from, r_to;
  float  len;
  int    count;
  int    dup; //number of the same node pair
  String shape;
  int    c;
  int    size;  
  PVector centr, V1, V2, V3;
  float  R;
  char   dir;

  Edge(Node from, Node to, String uid, String proj) {
    if (edgetrace) print("\tDEFINE Edge("+from.name+", "+to.name+")");
    if (from==null || to==null) return;
    this.uid   = uid;
    this.proj  = proj;
    this.from  = from;
    this.to    = to;
    Particle a = from.p;
    Particle b = to.p;
    if (a!=null && b!=null) this.s = physics.makeSpring(a, b, SPRING_STRENGTH, SPRING_DAMPING, SPRING_LENGTH);
    this.id    = from.id;
    this.to_id = to.id;
    this.len   = SPRING_LENGTH;
    this.count = 0;
  }

  void setID( String asc_id ) {
    this.asc_id = asc_id;
  }

  void setRole( String R, String F, String T ) {
    this.r_name = R;
    this.r_from = F;
    this.r_to  = T;
    String s   = "ARROW"; // Default edge shape
    int c      = Silver;  // Default edge color
    EdgeShape edge_shape = findEdgeShape(this.r_name);
    if (edge_shape != null) {
      s = edge_shape.s;
      c = edge_shape.c;
    }
    this.shape = s;
    this.c     = c;
    if (edgetrace) println("\tshape="+s+" color="+hex(c,6));
  } 

  void increment() {
    count++;
  }

  PVector vRotate(PVector To, PVector From, float theta, float r) {
    PVector P = new PVector(From.x, From.y);
    P.sub(To);
    float x   = cos(theta)*P.x - sin(theta)*P.y;
    float y   = sin(theta)*P.x + cos(theta)*P.y;
    PVector Q = new PVector(x, y);
    Q.normalize();
    Q.mult(r);
    Q.add(To);
    return Q;
  }
  
  float theta(PVector P, PVector Q) {
    //float sigma  = 0.1;
    float theta = 0;
    if (P==null || Q==null) return 0;
    float deltax = Q.x - P.x;
    float deltay = Q.y - P.y;
    float tan    = deltay/deltax;
    theta  = atan(tan);
    if (0 <= deltay) {
      if (sigma < deltax)                      theta = theta;
      if (-sigma <= deltax && deltax <= sigma) theta = HALF_PI;
      if (deltax < -sigma)                     theta = PI + theta;
    }
    else {
      if (deltax < -sigma)                     theta = PI + theta;
      if (-sigma <= deltax && deltax <= sigma) theta = PI + HALF_PI;
      if (sigma < deltax)                      theta = TWO_PI + theta;
    }
    return theta;
  }
  
  void drawLine(PVector P, PVector Q) {
    line(P.x, P.y, Q.x, Q.y);
  }
/*
  void fatLine(PVector P, PVector Q, float thickness) {
    float x1 = P.x, y1 = P.y;
    float x2 = Q.x, y2 = Q.y;
    float distance = dist(x1, y1, x2, y2);		
    float angle = 360-atan2((y2-y1),x2-x1) ; 
    pushMatrix();
    rectMode(CORNER);
    translate(x1,y1);
    rotate(360 - angle);
    rect(0, - (thickness / 2 ), distance, thickness);
    rectMode(CENTER);
    popMatrix();
  }
*/
  PVector calcCoord(PVector P) {
    if (P==null) return null;
    float x  = factor*P.x; //WORLD_SIZE_W/2 + factor*P.x; //Math.round( (P.x - WORLD_SIZE_W/2)/factor );
    float y  = factor*P.y; //WORLD_SIZE_H/2 + factor*P.y; //Math.round( (P.y - WORLD_SIZE_H/2)/factor );
    PVector Q = new PVector(x, y);
    return Q;
  }
  
  void drawArrow(/*Node from, Node to, int dup*/) {
    PVector From = new PVector(this.from.x, this.from.y);
    From.mult(factor);
    PVector To   = new PVector(this.to.x, this.to.y);
    To.mult(factor);
    //float sigma     = 0.1;
    int m           = 4; // number of control vertex for edge
    float w         = 0.7; // width of arrow
    float phai      = 0;
    float psi       = 0;
    float L         = dist(From.x, From.y, To.x, To.y)/2;
    PVector Q1;
    PVector M       = new PVector( (To.x + From.x)/2, (To.y + From.y)/2 );
    this.centr      = M;
    PVector Start, Terminate, P_i, Vi;
    PVector Outer[] = new PVector[m];
    PVector Inner[] = new PVector[m];
    int N           = 8;
    int n           = (dup%2==0)? dup/2 : -1*(dup/2)-1;
    if ( (From.x - To.x < -sigma) || 
         (-sigma <= From.x - To.x && From.x - To.x < sigma && 
          From.y - To.y < -sigma) )
      n *= -1; 
    if (n < 0)
      Q1 = vRotate(M, From, -HALF_PI, -n*L/N);
    else
      Q1 = vRotate(M, From, HALF_PI, n*L/N);
    float a = dist(M.x, M.y, Q1.x, Q1.y)/L;
    float b = (1-a*a)/(2*a);
    if (n < 0) {
      this.centr = vRotate(M, From, HALF_PI, b*L);
      psi = theta(centr, To) - theta(centr, From);
      if (PI < psi) psi -= TWO_PI;
      if (psi< -PI) psi += TWO_PI;
      phai = psi/m;
    }
    else if (n == 0){
      this.centr = vRotate(From, M, -HALF_PI, 1); 
    }
    else if (0 < n){
      this.centr = vRotate(M,From, -HALF_PI, b*L);
      psi = theta(centr, To) - theta(centr, From);
      if (PI < psi) psi -= TWO_PI;
      if (psi< -PI) psi += TWO_PI;
      phai = psi/m;
    }
    R = dist(centr.x, centr.y, From.x, From.y);  
    if (n == 0) {
      P_i = vRotate(From, M, 0, GAP);
      To  = vRotate(To,   M, 0, GAP);    
    }
    else {
      if (0 < phai) {
        P_i = vRotate(centr, From,  GAP/R, R);
        To  = vRotate(centr, To,   -GAP/R, R);
      }
      else {
        P_i = vRotate(centr, From, -GAP/R, R);
        To  = vRotate(centr, To,    GAP/R, R);
      }
    }
    Vi = new PVector(From.x - centr.x, From.y - centr.y);  
    Vi.normalize();  
    Vi.mult( m*w );
    Start     = PVector.add(P_i, Vi); 
    Terminate = PVector.sub(P_i, Vi);
    if (m==2) {
      P_i = Q1; 
      Vi  = new PVector(P_i.x - centr.x, P_i.y - centr.y);  
      Vi.normalize();  
      Vi.mult( w );
      Outer[1]= PVector.add(P_i, Vi); 
      Inner[1]= PVector.sub(P_i, Vi);    
    }
    else {
      for (int i=1; i < m; i++) {
        P_i = vRotate(centr, From, i*phai, R); 
        Vi  = new PVector(P_i.x - centr.x, P_i.y - centr.y);  
        Vi.normalize();  
        Vi.mult( (m-i)*w );
        Outer[i]= PVector.add(P_i, Vi); 
        Inner[i]= PVector.sub(P_i, Vi);
      }
    }
    this.V1 = Outer[1];
    this.V2 = Q1;
    this.V3 = Outer[m-1];
    //strokeJoin(MITER);
    if (this.shape==null || "ARROW".equals(this.shape)) {
      beginShape();
      if ( n == 0 ) {
        vertex(Start.x,     Start.y);
        vertex(To.x,        To.y);
        vertex(Terminate.x, Terminate.y);
        vertex(Start.x,     Start.y);
      }
      else {
        curveVertex(Start.x, Start.y);
        curveVertex(Start.x, Start.y);
        for (int j =1; j < m; j++) {
          curveVertex(Outer[j].x, Outer[j].y);
        }
        curveVertex(To.x, To.y);
        for (int k = m-1; k > 0; k--) {
          curveVertex(Inner[k].x, Inner[k].y);
        }
        curveVertex(Terminate.x, Terminate.y);
        curveVertex(Start.x, Start.y);
      }
      endShape();
    } 
    else if ("LINE".equals(this.shape)) {
      noFill();
      strokeWeight(4);
      if ( n == 0 ) {
        drawLine(From, To);
      }
      else {
        beginShape();
        curveVertex(From.x, From.y);        
        curveVertex(From.x, From.y);
        for (int j =1; j < m; j++) {
          curveVertex((Outer[j].x+Inner[j].x)/2,( Outer[j].y+Inner[j].y)/2);
        }
        curveVertex(To.x, To.y);        
        curveVertex(To.x, To.y);
        endShape();
      }
    }
    this.centr.mult(1/factor);
    this.V1.mult(1/factor);
    this.V2.mult(1/factor);
    this.V3.mult(1/factor);
    // draw edge degug info
    strokeWeight(0.5);
    if (debug) {
      fill(Pink);
      if (from_is !=null) text(this.from_is, From.x+20, From.y-30); 
      if (to_is!=null)    text(this.to_is,   To.x+20,   To.y-30);    
    }
    if (edgetrace) {
      fill(Red);
      float pX = factor * (pointX);
      float pY = factor * (pointY);
      if (!dragging) ellipse(pX, pY, GAP, GAP);

      fromX = factor * (from.x);
      fromY = factor * (from.y);
      toX   = factor * (to.x);
      toY   = factor * (to.y);
      if (dup>0) {
        cX    = factor * (this.centr.x);
        cY    = factor * (this.centr.y);
      }
      else {
        cX    = M.x;
        cY    = M.y;       
      }
      fill(Gray);
      stroke(Gray);
      line(cX, cY, fromX, fromY);
      line(cX, cY, toX, toY);
      textAlign(CORNER, CENTER);
      text(asc_id+" dup="+dup+" shape="+this.shape+" color="+hex(c,6), cX, cY); 
      PVector P    = new PVector(scrnX, scrnY);
      PVector Cntr = new PVector(cX, cY);
      if (P != null)  phai   = theta(Cntr, P);
      PVector P1   = new PVector(fromX, fromY);
      if (P1 != null) theta1 = theta(Cntr, P1);
      PVector P2   = new PVector(toX, toY);
      if (P2 != null) theta2 = theta(Cntr, P2);
      if (dup>0) {
        text("center x="+round(cX)+" y="+round(cY), cX, cY+10);
        text("theta1="+round(degrees(theta1))+" theta2="+round(degrees(theta2))+" phai="+round(degrees(phai)), cX, cY+20);
      }
    }
  }
  
  void show() {
    UserColor usercolor = (UserColor) usercolors.get(this.uid);
    strokeWeight(1.5);
    if (usercolor!=null) {
      u_clr = usercolor.c;
      stroke(u_clr);
    }
    else {
      u_clr = color(32*int(random(255)/32), 32*int(random(255)/32), 32*int(random(255)/32));
      while (u_clr == selectColor){
        u_clr = color(32*int(random(255)/32), 32*int(random(255)/32), 32*int(random(255)/32));          
      }
      newUserColor(uid, u_clr, "show");
      stroke(u_clr);        
    }
    strokeWeight(0.6);
    fill(this.c);
    drawArrow();   
  }

  void highlight() {
    Node from = this.from;    
    Node to   = this.to;
    if (from==null || to==null) return;
    float dx  = to.x - from.x;
    float dy  = to.y - from.y;
    float len = sqrt( dx*dx + dy*dy );
    float cs  = GAP * dx/len;
    float sn  = GAP * dy/len;
    float d   = 2;
    fromX = centerX + factor * (from.x - centerX);
    fromY = centerY + factor * (from.y - centerX);
    toX   = centerX + factor * (to.x  - centerX);
    toY   = centerY + factor * (to.y  - centerX);
    // draw highlighted edge
    strokeWeight(0.5);
    if (debug || edgetrace) {
      fill(Red);
      float pX = centerX + factor * (pointX  - centerX);
      float pY = centerY + factor * (pointY  - centerX);
      ellipse(pX, pY, GAP, GAP);
      fill(Gray);
      stroke(Gray);
      cX    = centerX + factor * (this.centr.x  - centerX);
      cY    = centerY + factor * (this.centr.y  - centerX);
      line(cX, cY, fromX, fromY);
      line(cX, cY, toX, toY);
      textAlign(CORNER, CENTER);
      if (dup==0)
        text(asc_id+" dup="+dup+" shape="+this.shape+" color="+hex(c,6), 20 + (fromX  + toX  )/2, 10 + (fromY  + toY  )/2);
      else
        text(asc_id+" dup="+dup+" shape="+this.shape+" color="+hex(c,6), cX, cY); 
    }
    stroke(selectColor);
    fill(selectColor);
    drawArrow();
    //draw label of roles
    fill(Red);
    textAlign(CENTER, CENTER);
    float diffY = toY - fromY;
    float D = 10;
    float dY = 0;
    if ( -D < diffY && diffY <= 0) dY = -D; 
    if (  0 < diffY && diffY < D ) dY =  D;
    if (dup==0){
      if (sel_edge.r_from != null)
        text( sel_edge.r_from, (3*fromX + toX  )/4, (3*fromY + toY  )/4 - dY );
      if (sel_edge.r_name != null)
        text( sel_edge.r_name, (fromX  + toX  )/2,  (fromY  + toY  )/2 );   
      if (sel_edge.r_to  != null)
        text( sel_edge.r_to,   (fromX  + 3*toX)/4,  (fromY  + 3*toY)/4 + dY );
    }
    else {
      text(sel_edge.r_from, factor*V1.x, factor*V1.y);
      text(sel_edge.r_name, factor*V2.x, factor*V2.y);
      text(sel_edge.r_to,   factor*V3.x, factor*V3.y);
    }
  }
}

void allUpdate() {
  Iterator itv = nodes.keySet().iterator();
  while (itv.hasNext ()) {
    String key = (String) itv.next();
    Node node = (Node) nodes.get(key);
    node.update();
  }
}

void allFix() {
  Iterator it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    String key = (String) it.next();
    Node node = (Node) nodes.get(key);
    node.fix();
  }
}

void allFree() {
  Iterator it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    String key = (String) it.next();
    Node node = (Node) nodes.get(key);
    if (!node.root)
      node.free();
  }
  damper = 1.0;
}

void allMove(float mx, float my) {
  Iterator it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    String key = (String) it.next();
    Node node = (Node) nodes.get(key);
    node.move(mx, my);
  }
}

void setEdgeValue(String asc_id, String r_name, String r_from, String r_to, String s, String clr) {
  if (edgetrace) println("setEdgeValue("+asc_id+","+r_name+","+r_from+","+r_to+","+s+","+clr+") ");
  Edge e = edges.get(asc_id);
  if (e!=null) {
    e.r_name = r_name;
    e.r_from = r_from;
    e.r_to   = r_to;
    if (s!=null && !"".equals(s))     e.shape  = s;
    if (clr!=null && !"".equals(clr)) e.c      = hexToColor(clr);
    if (edgetrace)  println("\tEdge UPDATED");
  }
}

Edge findEdge(String asc_id, String proj) {
  if (debug) print("\n\tfindEdge asc_id=" + asc_id + " proj=" + proj);
  Edge e = null;
  Iterator it = edges.keySet().iterator();
  while (it.hasNext ()) {
    String k  = (String) it.next();
    e = (Edge) edges.get(k);
    asc_id = asc_id.trim();
    proj   = proj.trim();
    if(k.equals(asc_id) && e.proj.equals(proj)) {
      if (debug) {
        if (debug) println("\tedge FOUND."); 
      }           
      return e;
    }
  }
  if (debug) println("\tedge NOT found.");
  return null;
}

void addEdge(String asc_id, String fromId, String fromProj, String toId, String toProj,
              String r_name, String r_from, String r_to, String uid, String proj) {
  if (edgetrace) println("================\naddEdge asc_id=" + asc_id +
                      " from id=" + fromId + " proj="+fromProj+" to id=" + toId +" proj="+toProj+ " uid=" + uid+ " proj=" + proj );
  if("".equals(fromId) || fromId == null || "".equals(toId) || toId == null) return;
  boolean newedge = false;
  char dir;
  String pair, reverse_pair;
  Node from = findNode(fromId, fromProj);
  if (from==null) from = newNode(fromId, fromId, "", uid, fromProj);
  Node to   = findNode(toId, toProj);
  if (to==null)   to   = newNode(toId,   toId, "", uid, toProj);
  if (edgetrace) println("\tfromId="+fromId+" toId="+toId);
  if(fromId.hashCode() < toId.hashCode()) {
    pair         = fromId+"#"+toId;
    reverse_pair = toId+"#"+fromId;
  }
  else {
    pair         = toId+"#"+fromId;    
    reverse_pair = fromId+"#"+toId;
  }
  if (edgetrace) println("\tpair="+pair+"\tasc_id="+asc_id);  
  from.update();    
  to.update();
  if ("".equals(asc_id)){
    newedge = true;
    asc_id = fromId+"#"+toId;
    Edge e = findEdge(asc_id, proj);
    if (e!=null) return;
  }  
  Edge the_edge  = new Edge(from, to, uid, proj);
  the_edge.asc_id = asc_id;
  int count_connection = 0;
  Iterator it = connected_edges.keySet().iterator();
  while (it.hasNext ()) {
    String k = (String) it.next();
    if ( k.indexOf(pair)==0 || k.indexOf(reverse_pair)==0 ) count_connection++;
  }
  if (newedge) the_edge.dup = 0; else the_edge.dup   = count_connection;
  the_edge.setRole(r_name, r_from, r_to);  
  if (edgetrace) println("\tthe_edge.setRole("+r_name+","+ r_from+","+ r_to+")");  
  if (edgetrace) println("\taddEdge edges put\t" + asc_id + "\t" + the_edge.from.name + "\t" + the_edge.to.name);   
  edges.put(asc_id, the_edge);
  if (edgetrace) println("\taddEdge connected_edges put\t" + pair + ":" + count_connection +"\t"+ asc_id);   
  connected_edges.put(pair + ":" + count_connection, asc_id);
  if (edgetrace) {
    println("\tconnected_edges size=" + connected_edges.size());   
    it = connected_edges.keySet().iterator();
    while (it.hasNext()) {
      String k = (String) it.next();
      println("\t\tlist\t" + k + "\t" + connected_edges.get(k));   
    }
  }
  if (edgetrace) println("================  addEdge");  
}

void addSpacersToNode( Node node ) {
  Iterator it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    String key = (String) it.next();
    Node n = (Node) nodes.get(key);
    Particle q = n.p;
    if ( node.p != q ) {
      physics.makeAttraction( q, node.p, -SPACER_STRENGTH, MIN_DISTANCE );
    }
  }
}

Node newNode(String id, String name, String grp, String uid, String proj) {
  if (nodetrace) print("----------------\nnewNode\tdefine\tid=" + id + " name=" + name + " grp=" + grp);
  if (id == null || id.equals("")) return null;
  if (name == null || name.equals("")) return null;
  String label = name;//unescapeSTR(name);
  Node node = findNode(id, proj);
  if (node == null) node = new Node(id, label, grp, uid, proj);
  node.name = label;
  node.grp  = grp;
  if (nodetrace) println("\tdefined\tid=" + node.id + " name=" + node.name + " grp=" + node.grp);
  addNode(node);
  if (nodetrace) println("----------------  newNode");
  return node;
}

void urlNode(String url, String id, String proj) {
  if (nodetrace) println("node="+id+" url="+url);
  Node node = findNode(id, proj);
  if (node != null && url != null) node.setURL(url);  
}
void addNode(Node node) {
  if (nodetrace) print("\t--------\n\taddNode id=" + node.id + " name=" + node.name);
  String the_id = node.id;
  String proj   = node.proj;
  Node the_node = findNode( the_id, proj ); 
  if (the_node!=null) { // increment used count
    if (nodetrace) println("\taddNode node Exists.");
    the_node.count += 1;
  } 
  else {                // add new node
    if (nodetrace) println("\taddNode ADD node.");
    nodes.put( the_id, node );
    addSpacersToNode( node );
  }
  if (nodetrace) {
    println("\tnodes size=" + nodes.size() );
    Iterator it = nodes.keySet().iterator();
    while (it.hasNext ()) {
      String key  = (String) it.next();
      Node n = (Node) nodes.get(key);
      println("\t\tlist\t" + key + "\t" + n.name);
    }
    println("\t--------  addNode");
  }
}

void printHex(String str) {
  char     chr;
  int      i;
  println(str);
  println("length="+str.length());
  for (i = 0; i < str.length(); i++) {
    chr = str.charAt(i) ;
    print(" " + hex((int)chr,4));
  } 
  print("\n");
}

Node findNode(String id, String proj) {
  if (nodetrace) print("\n\tfindNode id=" + id + " proj=" + proj);
  Node n = null;
  Iterator it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    String k  = (String) it.next();
    n = (Node) nodes.get(k);
    id = id.trim();
    proj=proj.trim();
    if(k.equals(id) && n.proj.equals(proj)) {
      if (nodetrace) {
        println("\tnode FOUND."); 
      }           
      return n;
    }
  }
  if (nodetrace) println("\tnode NOT found.");
  return null;
}

void selectNode(Node node) {
  if (nodetrace) println("selectNode(" + node.id + ")");
  String the_id  = node.id;
  topicX = Math.round( node.p.position.x );
  topicY = Math.round( node.p.position.y );
  if (node.vizgroup == null) 
    vizgroup.clear();
  else
    vizgroupNodes(node);
}

void infoNode(Node node) {
  //func =  "info node";
  String the_id   = node.id;
  String the_proj = node.proj;
  if (nodetrace) println("infoNode name=" + node.name + " id=" + the_id + " proj=" + the_proj);
  if ( javascript != null ) {
    javascript.selectnode(the_id, the_proj);
  }
}

void topicNode(Node node) {
  //func =  "topic node";
  String the_id  = node.id;
  if ( javascript != null ) {
    if (nodetrace) println("topicNode " + node.name + " (" + node.id + ")");
    javascript.topicnode(the_id);
  }
}

void setNodePosition(String id, String proj, int x, int y) {
  if (nodetrace) println("setNodePosition id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if (node!=null) node.position(x, y);
}

void fixNode(String id, String proj) {
  if (nodetrace) println("fixNode id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if (node!=null) node.fix();
}

void setNodeValue(String id, String proj, String name, String grp, String abst, String uid) {
  if (nodetrace) println("setNodeValue id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if ( node != null ) {
    if ( uid  != null && !uid.equals("") )  node.uid  = uid;  
    if ( name != null && !name.equals("") ) node.name = name;  
    if ( grp  != null && !grp.equals("") )  node.grp  = grp;  
    if ( abst != null && !abst.equals("") ) node.abst = abst;
  }
}

void setNodeName(String id, String proj, String name) {
  if (nodetrace) println("setNodeName id=" + id + " proj=" + proj + " name=" + name);
  Node node = findNode(id, proj);
  if ( node != null ) {
    if ( name != null && !name.equals("") ) node.name = name;  
  }
}

void setNodeAssoc(String id, String proj, int assoc) {
  if (nodetrace) println("setNodeAssoc id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if ( node!=null ) node.assoc = assoc;
  if ( nodetrace ) println("\tset node "+node.name+"("+id+")"+"\tassoc="+node.assoc);
}

void setNodeCount(String id, String proj, int count) {
  if (nodetrace) println("setNodeCount id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if ( node!=null ) node.count = count;
  if (nodetrace) println("\tset node "+node.name+"("+id+")"+"\tcount="+node.count);
}

void setNodeMass(String id, String proj, int mass) {
  if (nodetrace) println("setNodeMass id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if (node!=null) node.mass = mass;
  if (nodetrace) println("\tset node "+node.name+"("+id+")"+"\tmass="+node.mass);
}

void setNodeFreeDirection(String id, String proj, String d) {
  if (nodetrace) println("setNodeFreeDirection id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if ( node!=null ) {
    node.freeDirection = d; 
    node.p.freeDirection = d;
  }
}

void setNodeVizgroup(String id, String proj, String g) {
  if (nodetrace) println("setNodeVizgroup id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if ( node!=null ) {
    node.vizgroup = g; 
  }
}

void setNodeSize(String id, String proj, int size) {
  if (nodetrace) println("setNodeSize id=" + id + " proj=" + proj);
  Node node = findNode(id, proj);
  if ( node!=null ) node.size = size;
}

void newTopic() {
  if (debug) println("newTopic");
  if (javascript!=null) {
    javascript.new_topic();
  }
}

void pedia() {
  if (debug) println("pedia");
  if (javascript!=null) {
    javascript.pedia();
  }
}
/*
void setTopic(String oldid, String id, String name, String grp) {
  if (debug) println("setTopic oldid="+oldid+" id=" + id + " name=" + name + " grp=" + grp);
  //if (debug) println("setTopic id=" + id + " name=" + name + " grp=" + grp);
  name = name; //unescapeSTR( name );
  oldid  = oldid;
  id     = id;
  Node n = findNode(id);
  if (n == null) n = newNode(id, name, grp);
  if (debug) println( n.id );
  n.name = name;
  n.grp  = grp;
  n.fix();
  n.position(0, 0);
  ArrayList<Edge> connected = new ArrayList<Edge>();
  Node oldnode = findNode(oldid);
  if (oldnode!=null) {
    connected = connectedEdges(oldnode);
    for (int i = 0; i < connected.size(); i++) {
      Edge e = (Edge) connected.get(i);
      if (debug) println("\told:" + oldnode.name + " relation:" + e.from.name + " " + e.to.name);
      if ( oldnode.id.equals(e.from.id) ) {
        if (debug) println("\tnew:" + name + " relation:" + name + " " + e.to.name);
          e.from.id   = id;
          e.from.name = name;
          e.from.grp  = grp;
      }
      else if (oldnode.id.equals(e.to.id) ) {
        if (debug) println("\tnew:" + name + " relation:" + e.from.name + " " + name);
          e.to.id   = id;
          e.to.name = name;
          e.to.grp  = grp;
      }
    }
  }
  hideNode(oldnode);
}
*/
void newRelation(Node n) {
  if (debug) println("newRelation");
  //func =  "new relation";
  from_node = selected;
  selection = null;
  damper = 1.0;
}

void expandNode(Node node, String both) {
  //func =  "expand node";
  free = true;
  connected_edges.clear();
  String the_id   = node.id;  
  String the_proj = node.proj;
  fixNode(the_id, the_proj);
  if (javascript!=null) {
    javascript.startexpand();
    ArrayList<Edge> connected = new ArrayList<Edge>();
    connected = connectedEdges(node);
    the_adjacents = "(''";
    for (int i = 0; i < connected.size(); i++) {
      Edge e = (Edge) connected.get(i);
      String ajacent_id = "";
      if ( node.id.equals(e.from.id) ) {
        ajacent_id = e.to.id;
      }
      else if ( node.id.equals(e.to.id) ) {
        ajacent_id = e.from.id;        
      }
      the_adjacents += ",'"+ajacent_id+"'";
    }
    the_adjacents += ")";
    if (debug) println("expandNode name="+node.name+" id="+node.id+" proj="+node.proj+" ajacents="+the_adjacents);
    javascript.adjacentnodes(the_id, the_proj, the_adjacents, both);
    javascript.endexpand();
  }
  damper = 1.0;
}

void squeezeNode(Node node) {
  //func =  "squeeze node";
  ArrayList<Edge> connected = new ArrayList<Edge>();
  String from_is, to_is;
  from_is = to_is = "";
  String the_id = node.id;
  Node self, other;
  self = other = null;
  if (debug) println("squeezeNode " + the_id + " " + node.isa);
  if (!node.isa.equals("leaf")) {
    connected = connectedEdges(node);
    if (debug) println(connected.size() + " connected edge(s)");
    for (int i = 0; i < connected.size(); i++) {
      Edge e = (Edge) connected.get(i);
      if (the_id.equals(e.from.id)) {
        self    = e.from;
        from_is = e.from_is;
        other  = e.to;
        to_is  = e.to_is;
      } 
      else if (the_id.equals(e.to.id)) {
        self    = e.to;
        from_is = e.to_is;
        other  = e.from;
        to_is  = e.from_is;
      }
      if (debug) println("self="  + self.id  + ":" + self.isa  + "(" + from_is + ")\t" +
                         "other=" + other.id + ":" + other.isa + "(" + to_is + ")");
      if ( to_is != null && to_is.equals("leaf") ) {
        if (other.isa.equals("leaf")) {
          if (debug) println("remove edge " + e.asc_id + " leaf " + other.id);
          edges.remove(e.asc_id);
          nodes.remove(other.id);
          for (int j = 0; j < physics.numberOfAttractions(); j++) {
            Attraction attraction = physics.getAttraction(j);
            if (attraction.getOneEnd() == other.p || attraction.getTheOtherEnd() == other.p) {
              attraction.turnOff();
            }
          }
          for (int j = 0; j < physics.numberOfAttractions(); j++) {
            Attraction attraction = physics.getAttraction(j);
            if ( attraction.isOff() ) physics.removeAttraction(j);
          }
        } 
        else if (other.isa.equals("branch")) {
          if (debug) println("squeeze branch " + other.id);
          squeezeNode(other);
          edges.remove(e.asc_id);
          nodes.remove(other.id);
          for (int j = 0; j < physics.numberOfAttractions(); j++) {
            Attraction attraction = physics.getAttraction(j);
            if (attraction.getOneEnd() == other.p || attraction.getTheOtherEnd() == other.p) {
              attraction.turnOff();
            }
          }
          for (int j = 0; j < physics.numberOfAttractions(); j++) {
            Attraction attraction = physics.getAttraction(j);
            if ( attraction.isOff() ) physics.removeAttraction(j);
          }
        }
      }
    }
  }
}

void deleteTopic(String id, String proj) {
  if (debug) println("deleteTopic " + id);
  Node node = findNode(id, proj);
  if (node!=null) hideNode(node);
}

void hideNode(Node node) {
  if (debug) println("hideNode " + node.id + " " + node.isa);
  //func =  "hide node";
  if (node.vizgroup!=null) return;
  ArrayList<Edge> connected = new ArrayList<Edge>();
  connected = connectedEdges(node);
  //if (!node.root) {
  if (connected.size() > 0) squeezeNode(node);
  String the_id = node.id;
  if (debug) println(connected.size() + " connected edge(s)");
  if (connected !=null) {
    for (int i=0; i < connected.size(); i++) {
      Edge e = connected.get(i);
      edges.remove(e.asc_id);
    }
  }

  if (debug) println("\tRemove " + node.id + " " + node.name);
  nodes.remove(node.id);

  for (int i = 0; i < physics.numberOfAttractions(); i++) {
    Attraction attraction = physics.getAttraction(i);
    if (attraction.getOneEnd() == node.p || attraction.getTheOtherEnd() == node.p) {
      attraction.turnOff();
    }
  }
  for (int i = 0; i < physics.numberOfAttractions(); i++) {
    Attraction attraction = physics.getAttraction(i);
    if ( attraction.isOff() ) physics.removeAttraction(i);
  }
  //}
}

void markNode() {
  //Object k;
  String k;
  String key;
  Node  node;
  Edge  edge;
  int    count;
  Iterator it;
  ArrayList<Node> adjacent = new ArrayList<Node>();
  // clear node isa status
  it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    key = (String) it.next();
    node = (Node) nodes.get(key);
    node.isa = "";
    node.count = 0;
    nodes.put(key, node);
  }
  // reset node conecctioncounts
  it = edges.keySet().iterator();
  while (it.hasNext ()) {
    k = (String) it.next();
    edge = (Edge) edges.get(k);
    key = edge.from.id;
    node = (Node) nodes.get(key);
    node.count += 1;
    key = edge.to.id;
    node = (Node) nodes.get(key);
    node.count += 1;
  }
  it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    key = (String) it.next();
    node = (Node) nodes.get(key);
    // A node is leaf iff a node is connected to one edge.
    count = node.count;
    if (!node.root && node.isa.equals("") && count == 1) {
      node.isa = "leaf";
      //if (debug) print(key + " is leaf.\n");
      nodes.put(key, node);
    }
    // A node is trunk iff a node is root.
    if (node.root) {
      node.isa = "trunk";
      //if (debug) print(key + " is trunk.\n");
      nodes.put(key, node);
      // A node is trunk iff a node is connected directry to root node.
      adjacent = adjacentNodes(node);
      for (int i = 0; i < adjacent.size(); i++) {
        Node n = (Node) adjacent.get(i);
        key = n.id;
        n.isa = "trunk";
        //if (debug) print(key + " is trunk connected to root.\n");
        nodes.put(key, n);
      }
    }
  }
  // repeat 3 times
  int repeat = 0;
  while (repeat++ <= 3) {
    it = nodes.keySet().iterator();
    while (it.hasNext ()) {
      key = (String) it.next();
      node = (Node) nodes.get(key);
      if (node.isa.equals("")); 
      {
        // A node is branch iff a node is connected directry to n-1 leaf and/or branch nodes.
        // A node is trunk iff a node is connected directry to more than 2 trunk nodes.
        adjacent = adjacentNodes(node);
        int branch_count, trunc_count;
        branch_count = trunc_count = 0;
        for (int i = 0; i < adjacent.size(); i++) {
          Node n = (Node) adjacent.get(i);
          if (n.isa.equals("leaf") || n.isa.equals("branch"))  branch_count++;
          else if (n.isa.equals("trunk")) trunc_count++;
        }
        if (node.isa.equals("") && branch_count == adjacent.size() - 1) {
          node.isa = "branch";
          //if (debug) print(key + " is branch.(Step2)\n");
          nodes.put(key, node);
        } 
        else if (node.isa.equals("") && trunc_count >= 2) {
          node.isa = "trunk";
          //if (debug) print(key + " is trunk.(Step2)\n");
          nodes.put(key, node);
        }
      }
    }
  }
  // A node is trunk if a node is neigther leaf nor branch i.e. "".
  it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    key = (String) it.next();
    node = (Node) nodes.get(key);
    if (node.isa.equals("")) {
      node.isa = "trunk";
      //if (debug) print(key + " is trunk.(Step3)\n");
      nodes.put(key, node);
    }
  }
}

ArrayList<Node> adjacentNodes(Node node) {
  ArrayList<Node> adjacent = new ArrayList<Node>();
  String the_id = node.id;
  Iterator it = edges.keySet().iterator();
  while (it.hasNext ()) {
    Object k = it.next();
    Edge e = (Edge) edges.get(k);
    Node other = null;
    if (the_id.equals(e.from.id)) {
      other = e.to;
    } 
    else if (the_id.equals(e.to.id)) {
      other = e.from;
    }
    if (other != null) adjacent.add(other);
  }
  return adjacent;
}

void vizgroupNodes(Node node) {
  vizgroup.clear();
  String the_vizgroup = node.vizgroup;
  Iterator it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    String key = (String) it.next();
    Node n = (Node) nodes.get(key);
    if (the_vizgroup.equals(n.vizgroup)) {
      if (n != null) vizgroup.add(n);
    }
  }
}

void rootNode(Node node) {
  if (node == null) {
    if (debug) println("EMPTY node");
    return;
  }
  if (debug) {
    println("rootNode " + node.name);
  }
  physics.clear();
  edges.clear();
  nodes.clear();
  usercolors.clear();
  node.root = true;
  node.fix();
  node.position(0, 0);
  node.c = rootColor;
  addNode(node);
  if ( javascript != null ) {
    //javascript.countassoc(node.id, node.proj);
  }  
}

void markEdge() {
  Iterator it;
  Object k;
  Edge edge;
  Node node;
  String key;
  ArrayList<Edge> connected = new ArrayList<Edge>();
  int count;
  // clear node isa status
  it = edges.keySet().iterator();
  while (it.hasNext ()) {
    key = (String) it.next();
    edge = (Edge) edges.get(key);
    edge.from_is = "";
    edge.to_is  = "";
    edges.put(key, edge);
  }
  it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    k = it.next();
    node = (Node) nodes.get(k);
    String the_id = node.id;
    if (node.isa.equals("leaf")) {
      checkEdges(node);
    }
  }
  it = nodes.keySet().iterator();
  while (it.hasNext ()) {
    k = it.next();
    node = (Node) nodes.get(k);
    String the_id = node.id;
    if (!node.isa.equals("leaf")) {
      checkEdges(node);
    }
  }
}

void checkEdges(Node node) {
  String the_id = node.id;
  ArrayList<Edge> connected = new ArrayList<Edge>();
  connected = connectedEdges(node);
  for (int i = 0; i < connected.size(); i++) {
    Edge edge = (Edge) connected.get(i);
    Node from = edge.from;
    Node to = edge.to;
    if (the_id.equals(from.id) && edge.from_is.equals("")) {
      if (from.isa.equals("leaf")) {
        edge.from_is = "leaf";
        edge.to_is  = "trunk";
      } 
      else if (from.isa.equals("branch") && 
        (to.isa.equals("trunk") || to.isa.equals("root")) ) {
        edge.from_is = "leaf";
        edge.to_is  = "trunk";
      }
    } 
    else if (the_id.equals(to.id) && edge.to_is.equals("")) {
      if (to.isa.equals("leaf")) {
        edge.to_is  = "leaf";
        edge.from_is = "trunk";
      } 
      else if (to.isa.equals("branch") && 
        (from.isa.equals("trunk") || from.isa.equals("root")) ) {
        edge.to_is  = "leaf";
        edge.from_is = "trunk";
      }
    }
  }
  boolean found_trunk = false;
  boolean found_leaf = false;
  for (int i = 0; i < connected.size(); i++) {
    Edge edge = (Edge) connected.get(i);
    Node from = edge.from;
    Node to = edge.to;
    if ( (the_id.equals(from.id) && edge.from_is.equals("leaf")) ||
      (the_id.equals(to.id)  && edge.to_is.equals("leaf")) ) {  
      found_trunk = true;
    }
    if ( (the_id.equals(from.id) && edge.from_is.equals("trunk")) ||
      (the_id.equals(to.id)  && edge.to_is.equals("trunk")) ) {  
      found_leaf = true;
    }
  }
  for (int i = 0; i < connected.size(); i++) {
    Edge edge = (Edge) connected.get(i);
    Node from = edge.from;
    Node to = edge.to;
    if (the_id.equals(from.id) && edge.from_is.equals("")) { 
      if (from.isa.equals("branch") && to.isa.equals("branch")) {
        if (found_leaf) {
          edge.from_is = "leaf";
          edge.to_is  = "trunk";
        }
        if (found_trunk) {
          edge.from_is = "trunk";
          edge.to_is  = "leaf";
        }
      }
    } 
    else if (the_id.equals(to.id) && edge.to_is.equals("")) { 
      if (to.isa.equals("branch") && from.isa.equals("branch")) {
        if (found_leaf) {
          edge.to_is  = "leaf";
          edge.from_is = "trunk";
        }
        if (found_trunk) {
          edge.to_is  = "trunk";        
          edge.from_is = "leaf";
        }
      }
    }
  }
}

void infoEdge(Edge e) {
  if (debug) println("infoEdge " + e.asc_id);
  //func =  "info edge";
  String asc_id  = e.asc_id;
  if ( javascript != null ) {
    javascript.selectedge(asc_id);
  }
}

void removeEdge(String asc_id) {
  Edge e = edges.get(asc_id);
  hideEdge(e); 
}

void hideEdge(Edge e) { //TODO renumber duplicate eges between the same node pair.
  if (debug) println("hideEdge " + e.asc_id);
  String asc_id = e.asc_id;
  Node from     = e.from;  
  Node to       = e.to;
  edges.remove(asc_id);
  Iterator it = edges.keySet().iterator();
  int count_from=0;  
  int count_to=0;
  while (it.hasNext ()) {
    Object k = it.next();
    Edge edge = (Edge) edges.get(k);
    if (edge.from.id.equals(from.id) || edge.to.id.equals(from.id)) {
      count_from++;
    }
    if (edge.from.id.equals(to.id) || edge.to.id.equals(to.id)) {
      count_to++;
    }
  }
  if (count_from == 0) {
    squeezeNode(from);
    hideNode(from);
  }
  else {
    from.count = count_from;
    from.assoc -= 1;
  }
  if (count_to == 0) {
    squeezeNode(to);
    hideNode(to);    
  }
  else {
    to.count = count_to;
    to.assoc -= 1;
  }
}

ArrayList<Edge> connectedEdges(Node node) {
  ArrayList<Edge> connected = new ArrayList<Edge>();
  String the_id = node.id;
  Iterator it = edges.keySet().iterator();
  while (it.hasNext ()) {
    Object k = it.next();
    Edge edge = (Edge) edges.get(k);
    if (the_id.equals(edge.from.id) || the_id.equals(edge.to.id)) {
      connected.add(edge);
    }
  }
  return connected;
}

ArrayList<Node> connectedNodes(Node node) {
  ArrayList<Node> connected = new ArrayList<Node>();
  String the_id = node.id;
  Iterator it = edges.keySet().iterator();
  while (it.hasNext ()) {
    Object k = it.next();
    Edge e = (Edge) edges.get(k);
    if ( the_id.equals(e.from.id) )
      connected.add(e.to);
    else if ( the_id.equals(e.to.id) )
      connected.add(e.from);
  }
  return connected;
}

