mev-4.0.01/source/org/tigr/microarray/mev/cluster/gui/helpers/ktree/Ktree.java

Code
Comments
Other
Rev Date Author Line
2 26 Feb 07 jari 1 /*
2 26 Feb 07 jari 2 Copyright @ 1999-2004, The Institute for Genomic Research (TIGR).
2 26 Feb 07 jari 3 All rights reserved.
2 26 Feb 07 jari 4  */
2 26 Feb 07 jari 5 /*
2 26 Feb 07 jari 6  * Ktree.java
2 26 Feb 07 jari 7  *
2 26 Feb 07 jari 8  * Created on August 11, 2004, 9:56 AM
2 26 Feb 07 jari 9  */
2 26 Feb 07 jari 10
2 26 Feb 07 jari 11 package org.tigr.microarray.mev.cluster.gui.helpers.ktree;
2 26 Feb 07 jari 12
2 26 Feb 07 jari 13 import java.awt.AlphaComposite;
2 26 Feb 07 jari 14 import java.awt.Color;
2 26 Feb 07 jari 15 import java.awt.Composite;
2 26 Feb 07 jari 16 import java.awt.Dimension;
2 26 Feb 07 jari 17 import java.awt.Graphics;
2 26 Feb 07 jari 18 import java.awt.Graphics2D;
2 26 Feb 07 jari 19 import java.awt.Point;
2 26 Feb 07 jari 20 import java.awt.RenderingHints;
2 26 Feb 07 jari 21 import java.awt.geom.CubicCurve2D;
2 26 Feb 07 jari 22 import java.util.Vector;
2 26 Feb 07 jari 23
2 26 Feb 07 jari 24 import javax.swing.JPanel;
2 26 Feb 07 jari 25
2 26 Feb 07 jari 26 /**
2 26 Feb 07 jari 27  *
2 26 Feb 07 jari 28  * @author  braisted
2 26 Feb 07 jari 29  */
2 26 Feb 07 jari 30 public class Ktree extends JPanel {
2 26 Feb 07 jari 31     
2 26 Feb 07 jari 32     
2 26 Feb 07 jari 33     /**
2 26 Feb 07 jari 34      * Array of tree nodes.  Nodes should be represeted by level.
2 26 Feb 07 jari 35      * Ordering represents left to right order
2 26 Feb 07 jari 36      */
2 26 Feb 07 jari 37     
2 26 Feb 07 jari 38     private ITreeNode [][] nodes;
2 26 Feb 07 jari 39     
2 26 Feb 07 jari 40     /**
2 26 Feb 07 jari 41      * Root node
2 26 Feb 07 jari 42      */
2 26 Feb 07 jari 43     private ITreeNode root;
2 26 Feb 07 jari 44     private int xMargin = 15;
2 26 Feb 07 jari 45     private int yMargin = 15;
2 26 Feb 07 jari 46     private int maxWidth = 0;
2 26 Feb 07 jari 47     
2 26 Feb 07 jari 48     private int interNodeHeight = 40;
2 26 Feb 07 jari 49     private int interNodeWidth = 15;
2 26 Feb 07 jari 50     
2 26 Feb 07 jari 51     private boolean nodeSelected = false;
2 26 Feb 07 jari 52     private boolean isStraitConnector = false;
2 26 Feb 07 jari 53     
2 26 Feb 07 jari 54     Vector selectedPathNodes;
2 26 Feb 07 jari 55     ITreeNode selectedNode;
2 26 Feb 07 jari 56     boolean isNodeSelected;
2 26 Feb 07 jari 57     
2 26 Feb 07 jari 58     /** Creates a new instance of Ktree */
2 26 Feb 07 jari 59     public Ktree(ITreeNode root) {
2 26 Feb 07 jari 60         nodes = new ITreeNode[1][1];
2 26 Feb 07 jari 61         nodes[0][0] = root;
2 26 Feb 07 jari 62         this.root = root;
2 26 Feb 07 jari 63         this.selectedPathNodes = new Vector();
2 26 Feb 07 jari 64         init();
2 26 Feb 07 jari 65     }
2 26 Feb 07 jari 66     
2 26 Feb 07 jari 67     /** Creates a new instance of Ktree with the provided node array structure */
2 26 Feb 07 jari 68     public Ktree(ITreeNode [][] data) {
2 26 Feb 07 jari 69         nodes = data;
2 26 Feb 07 jari 70         root = nodes[0][0];
2 26 Feb 07 jari 71         this.selectedPathNodes = new Vector();
2 26 Feb 07 jari 72         init();
2 26 Feb 07 jari 73     }
2 26 Feb 07 jari 74     
2 26 Feb 07 jari 75     /** Creates a new instance of Ktree with the provided node data as an array of
2 26 Feb 07 jari 76      * Vectors.  One vector for each level in the tree.  Nodes know the relationships.
2 26 Feb 07 jari 77      */
2 26 Feb 07 jari 78     public Ktree(Vector [] data) {
2 26 Feb 07 jari 79         init();
2 26 Feb 07 jari 80         this.selectedPathNodes = new Vector();
2 26 Feb 07 jari 81     }
2 26 Feb 07 jari 82     
2 26 Feb 07 jari 83     private void init() {
2 26 Feb 07 jari 84         setBackground(Color.white);
2 26 Feb 07 jari 85         updateSize();
2 26 Feb 07 jari 86        /* getMaxLevelWidth();
2 26 Feb 07 jari 87         setSize( 2*xMargin + maxWidth*(interNodeWidth + root.getWidth()) - interNodeWidth,
2 26 Feb 07 jari 88         2*yMargin + nodes.length*(interNodeHeight + root.getHeight()) - interNodeHeight);
2 26 Feb 07 jari 89         setPreferredSize( new Dimension( 2*xMargin + maxWidth*(interNodeWidth + root.getWidth()) - interNodeWidth,
2 26 Feb 07 jari 90         2*yMargin + nodes.length*(interNodeHeight + root.getHeight()) - interNodeHeight));
2 26 Feb 07 jari 91         **/
2 26 Feb 07 jari 92     }
2 26 Feb 07 jari 93     
2 26 Feb 07 jari 94     public boolean addNode(ITreeNode [] parentNodes, ITreeNode childNodetoAdd, int levl) {
2 26 Feb 07 jari 95         return true;
2 26 Feb 07 jari 96     }
2 26 Feb 07 jari 97     
2 26 Feb 07 jari 98     
2 26 Feb 07 jari 99     public boolean deleteNode(ITreeNode node) {
2 26 Feb 07 jari 100         return true;
2 26 Feb 07 jari 101     }
2 26 Feb 07 jari 102     
2 26 Feb 07 jari 103     public void setNodes(ITreeNode [][] newNodes) {
2 26 Feb 07 jari 104         this.nodes = newNodes;
2 26 Feb 07 jari 105     }
2 26 Feb 07 jari 106     
2 26 Feb 07 jari 107     private void minimizeBranchingOverlap() {
2 26 Feb 07 jari 108         //Iterative implementation to minimize the crossing of connectors
2 26 Feb 07 jari 109         //Top down
2 26 Feb 07 jari 110         //Traverse a level, list ordering of child nodes, swap child nodes
2 26 Feb 07 jari 111         //to minimize cross overrs.
2 26 Feb 07 jari 112         //child nodes should be sorted within each child, assess child order and swap
2 26 Feb 07 jari 113         //accordingly to minimize crossing.
2 26 Feb 07 jari 114     }
2 26 Feb 07 jari 115     
2 26 Feb 07 jari 116     public ITreeNode getRoot() {
2 26 Feb 07 jari 117         return this.root;
2 26 Feb 07 jari 118     }
2 26 Feb 07 jari 119     
2 26 Feb 07 jari 120     public int getMaxLevelWidth() {
2 26 Feb 07 jari 121         int max = -1;
2 26 Feb 07 jari 122         for(int i = 0; i < nodes.length; i++) {
2 26 Feb 07 jari 123             max = Math.max(max, nodes[i].length);
2 26 Feb 07 jari 124         }
2 26 Feb 07 jari 125         maxWidth = max;
2 26 Feb 07 jari 126         return max;
2 26 Feb 07 jari 127     }
2 26 Feb 07 jari 128     
2 26 Feb 07 jari 129     public Vector getSelectedPathNodes() {
2 26 Feb 07 jari 130         return selectedPathNodes;
2 26 Feb 07 jari 131     }
2 26 Feb 07 jari 132     
2 26 Feb 07 jari 133     public int getTreePixelWidth() {
2 26 Feb 07 jari 134         int levelWidth = getMaxLevelWidth();
2 26 Feb 07 jari 135         int treeWidth = 2*xMargin + levelWidth * (interNodeWidth + nodes[0][0].getWidth()) - interNodeWidth;
2 26 Feb 07 jari 136         return treeWidth;
2 26 Feb 07 jari 137     }
2 26 Feb 07 jari 138     
2 26 Feb 07 jari 139     public int getTreePixelHeight() {
2 26 Feb 07 jari 140         int levelHeight = nodes.length;
2 26 Feb 07 jari 141         int treeHeight = 2*xMargin + levelHeight * (interNodeHeight + nodes[0][0].getHeight()) - interNodeHeight;
2 26 Feb 07 jari 142         return treeHeight;
2 26 Feb 07 jari 143     }    
2 26 Feb 07 jari 144     
2 26 Feb 07 jari 145     public void setStraightConnectorStyle(boolean isStraitConn) {
2 26 Feb 07 jari 146         this.isStraitConnector = isStraitConn;
2 26 Feb 07 jari 147     }
2 26 Feb 07 jari 148     
2 26 Feb 07 jari 149     public void setInterNodeHeight(int height) {
2 26 Feb 07 jari 150         this.interNodeHeight = height;
2 26 Feb 07 jari 151     }
2 26 Feb 07 jari 152     
2 26 Feb 07 jari 153     public void setInterNodeWidth(int width) {
2 26 Feb 07 jari 154         this.interNodeWidth = width;
2 26 Feb 07 jari 155     }
2 26 Feb 07 jari 156     
2 26 Feb 07 jari 157     public void setSelectedNode(ITreeNode node) {
2 26 Feb 07 jari 158         this.selectedNode = node;
2 26 Feb 07 jari 159         this.isNodeSelected = true;
2 26 Feb 07 jari 160     }
2 26 Feb 07 jari 161     
2 26 Feb 07 jari 162     public void setSelectionPaths(Vector nodes) {
2 26 Feb 07 jari 163         this.selectedPathNodes = nodes;
2 26 Feb 07 jari 164     }
2 26 Feb 07 jari 165     
2 26 Feb 07 jari 166     public void clearSelection() {
2 26 Feb 07 jari 167         this.isNodeSelected = false;
2 26 Feb 07 jari 168         this.selectedPathNodes = new Vector();
2 26 Feb 07 jari 169     }
2 26 Feb 07 jari 170     
2 26 Feb 07 jari 171     public boolean checkSelection(int x, int y, int selectionPolarity) {
2 26 Feb 07 jari 172         this.selectedNode = getSelectedNode(x,y);
2 26 Feb 07 jari 173         if(this.selectedNode == null) {
2 26 Feb 07 jari 174             clearSelection();
2 26 Feb 07 jari 175             return false;
2 26 Feb 07 jari 176         } else {
2 26 Feb 07 jari 177             this.isNodeSelected = true;
2 26 Feb 07 jari 178             setSelectionPaths(getPathNodes(this.selectedNode, selectionPolarity));
2 26 Feb 07 jari 179         }
2 26 Feb 07 jari 180         return true;
2 26 Feb 07 jari 181     }
2 26 Feb 07 jari 182     
2 26 Feb 07 jari 183     public ITreeNode getSelectedNode() {
2 26 Feb 07 jari 184         return selectedNode;
2 26 Feb 07 jari 185     }
2 26 Feb 07 jari 186     
2 26 Feb 07 jari 187     private ITreeNode getSelectedNode(int x, int y) {
2 26 Feb 07 jari 188         ITreeNode node = null;
2 26 Feb 07 jari 189         int level = (int)((y-yMargin)/(interNodeHeight + nodes[0][0].getHeight()));
2 26 Feb 07 jari 190         
2 26 Feb 07 jari 191         if(level < 0 || level >= nodes.length)
2 26 Feb 07 jari 192             return null;
2 26 Feb 07 jari 193         
2 26 Feb 07 jari 194         for(int i = 0; i < nodes[level].length; i++) {
2 26 Feb 07 jari 195             if(nodes[level][i].contains(x,y)) {
2 26 Feb 07 jari 196                 node = nodes[level][i];
2 26 Feb 07 jari 197                 break;
2 26 Feb 07 jari 198             }
2 26 Feb 07 jari 199         }
2 26 Feb 07 jari 200         return node;
2 26 Feb 07 jari 201     }
2 26 Feb 07 jari 202     
2 26 Feb 07 jari 203     public Vector getPathNodes(ITreeNode node, int polarity) {
2 26 Feb 07 jari 204         Vector ancestors, successors;
2 26 Feb 07 jari 205         
2 26 Feb 07 jari 206         if(polarity == 0) {  //get both directions
2 26 Feb 07 jari 207             ancestors = new Vector();
2 26 Feb 07 jari 208             successors = new Vector();
2 26 Feb 07 jari 209             ancestors.addElement(node);
2 26 Feb 07 jari 210             node.getAncestors(ancestors);
2 26 Feb 07 jari 211             node.getSuccessors(successors);
2 26 Feb 07 jari 212
2 26 Feb 07 jari 213             //merge results
2 26 Feb 07 jari 214             for(int i = 0; i < successors.size(); i++) {
2 26 Feb 07 jari 215                 ancestors.addElement(successors.elementAt(i));
2 26 Feb 07 jari 216             }
2 26 Feb 07 jari 217             return ancestors;  //merged
2 26 Feb 07 jari 218         } else if(polarity == 1) { //ancestors only
2 26 Feb 07 jari 219             ancestors = new Vector();
2 26 Feb 07 jari 220             ancestors.addElement(node);
2 26 Feb 07 jari 221             node.getAncestors(ancestors);
2 26 Feb 07 jari 222             return ancestors;
2 26 Feb 07 jari 223         } else if(polarity == 2) {
2 26 Feb 07 jari 224             successors = new Vector();
2 26 Feb 07 jari 225             successors.addElement(node);
2 26 Feb 07 jari 226             node.getSuccessors(successors);
2 26 Feb 07 jari 227             return successors;
2 26 Feb 07 jari 228         }
2 26 Feb 07 jari 229         return new Vector();  //default return empty vector
2 26 Feb 07 jari 230     }
2 26 Feb 07 jari 231     
2 26 Feb 07 jari 232     public void updateSize() {
2 26 Feb 07 jari 233         int width = getTreePixelWidth();
2 26 Feb 07 jari 234         int height = getTreePixelHeight();
2 26 Feb 07 jari 235         setPreferredSize(new Dimension(width, height));
2 26 Feb 07 jari 236         setSize(width, height);
2 26 Feb 07 jari 237     }
2 26 Feb 07 jari 238     
2 26 Feb 07 jari 239     public void paint(Graphics g) {
2 26 Feb 07 jari 240         super.paint(g);
2 26 Feb 07 jari 241         Graphics2D g2 = (Graphics2D)g;
2 26 Feb 07 jari 242         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
2 26 Feb 07 jari 243         int nodeWidth = root.getWidth();
2 26 Feb 07 jari 244         int nodeHeight = root.getHeight();
2 26 Feb 07 jari 245         int maxLevelWidth = (maxWidth)*( nodeWidth + interNodeWidth) - interNodeWidth;
2 26 Feb 07 jari 246         
2 26 Feb 07 jari 247         int currX = xMargin + (maxLevelWidth - nodeWidth)/2;
2 26 Feb 07 jari 248         int currY = yMargin;
2 26 Feb 07 jari 249         int levelWidth;
2 26 Feb 07 jari 250         boolean selected = false;
2 26 Feb 07 jari 251         
2 26 Feb 07 jari 252         //Start with the root
2 26 Feb 07 jari 253         if(this.selectedNode != null && this.selectedNode == nodes[0][0])
2 26 Feb 07 jari 254             ((ITreeNodeRenderer)nodes[0][0]).renderNode(g2, currX, currY, ITreeNodeRenderer.SELECTED_NODE);
2 26 Feb 07 jari 255         else if(selectedPathNodes.contains(nodes[0][0]))
2 26 Feb 07 jari 256             ((ITreeNodeRenderer)nodes[0][0]).renderNode(g2, currX, currY, ITreeNodeRenderer.PATH_NODE);
2 26 Feb 07 jari 257         else if(this.selectedNode != null)
2 26 Feb 07 jari 258             ((ITreeNodeRenderer)nodes[0][0]).renderNode(g2, currX, currY, ITreeNodeRenderer.NON_PATH_NODE);            
2 26 Feb 07 jari 259         else
2 26 Feb 07 jari 260             ((ITreeNodeRenderer)nodes[0][0]).renderNode(g2, currX, currY, ITreeNodeRenderer.STANDARD_NODE);
2 26 Feb 07 jari 261         
2 26 Feb 07 jari 262         for(int i = 1; i < nodes.length; i++) {
2 26 Feb 07 jari 263             //update y for level
2 26 Feb 07 jari 264             
2 26 Feb 07 jari 265             currY += (nodeHeight + interNodeHeight);
2 26 Feb 07 jari 266             //update initial x for the level
2 26 Feb 07 jari 267             levelWidth = (nodes[i].length)*( nodeWidth + interNodeWidth) - interNodeWidth;
2 26 Feb 07 jari 268             currX = xMargin+(maxLevelWidth - levelWidth)/2;
2 26 Feb 07 jari 269             for(int j = 0; j < nodes[i].length; j++) {
2 26 Feb 07 jari 270                 
2 26 Feb 07 jari 271                 if(this.selectedNode != null && this.selectedNode == nodes[i][j])
2 26 Feb 07 jari 272                     ((ITreeNodeRenderer)nodes[i][j]).renderNode(g2, currX, currY, ITreeNodeRenderer.SELECTED_NODE);
2 26 Feb 07 jari 273                 else if(selectedPathNodes.contains(nodes[i][j]))
2 26 Feb 07 jari 274                     ((ITreeNodeRenderer)nodes[i][j]).renderNode(g2, currX, currY, ITreeNodeRenderer.PATH_NODE);
2 26 Feb 07 jari 275                 else if(this.selectedNode != null)
2 26 Feb 07 jari 276                     ((ITreeNodeRenderer)nodes[i][j]).renderNode(g2, currX, currY, ITreeNodeRenderer.NON_PATH_NODE);                    
2 26 Feb 07 jari 277                 else
2 26 Feb 07 jari 278                     ((ITreeNodeRenderer)nodes[i][j]).renderNode(g2, currX, currY, ITreeNodeRenderer.STANDARD_NODE);
2 26 Feb 07 jari 279                 
2 26 Feb 07 jari 280                 //update x pos for each added node
2 26 Feb 07 jari 281                 currX += nodeWidth + interNodeWidth;
2 26 Feb 07 jari 282             }
2 26 Feb 07 jari 283         }
2 26 Feb 07 jari 284         renderConnectors(g);
2 26 Feb 07 jari 285     }
2 26 Feb 07 jari 286     
2 26 Feb 07 jari 287     private void renderConnectors(Graphics g) {
2 26 Feb 07 jari 288         ITreeNode currNode;
2 26 Feb 07 jari 289         ITreeNode [] children;
2 26 Feb 07 jari 290         Point start, finish;
2 26 Feb 07 jari 291         boolean selected = false;
2 26 Feb 07 jari 292         Color origColor = g.getColor();
2 26 Feb 07 jari 293         
2 26 Feb 07 jari 294         Graphics2D g2 = (Graphics2D)g;
2 26 Feb 07 jari 295         Composite composite = g2.getComposite();
2 26 Feb 07 jari 296         
2 26 Feb 07 jari 297         for(int i = 0; i < nodes.length; i++) {
2 26 Feb 07 jari 298             //  for(int i = 0; i < 3; i++) {
2 26 Feb 07 jari 299             
2 26 Feb 07 jari 300             //  System.out.println("render connectors");
2 26 Feb 07 jari 301             for(int j = 0; j < nodes[i].length; j++) {
2 26 Feb 07 jari 302                 //     System.out.println("getChildren");
2 26 Feb 07 jari 303                 currNode = (nodes[i][j]);
2 26 Feb 07 jari 304                 children = currNode.getChildren();
2 26 Feb 07 jari 305                 
2 26 Feb 07 jari 306                 
2 26 Feb 07 jari 307                 if(children == null) {
2 26 Feb 07 jari 308                     //         System.out.println("null children");
2 26 Feb 07 jari 309                     // continue;
2 26 Feb 07 jari 310                     break;
2 26 Feb 07 jari 311                 }
2 26 Feb 07 jari 312
2 26 Feb 07 jari 313 /*
2 26 Feb 07 jari 314                 if(i == nodes.length-1 && j == nodes[i].length-1) {
2 26 Feb 07 jari 315                     System.out.println("rendering final connector");
2 26 Feb 07 jari 316                     System.out.println("child count "+children.length);
2 26 Feb 07 jari 317                     if(children.length > 0) {
2 26 Feb 07 jari 318                         System.out.println("Child go term = "+ ((org.tigr.microarray.mev.cluster.gui.impl.ease.gotree.GONode)children[0]).getTerm());
2 26 Feb 07 jari 319                         System.out.println("child depth = "+((org.tigr.microarray.mev.cluster.gui.impl.ease.gotree.GONode)children[0]).getLevel());
2 26 Feb 07 jari 320                     }
2 26 Feb 07 jari 321                 }
2 26 Feb 07 jari 322 */
2 26 Feb 07 jari 323                 
2 26 Feb 07 jari 324                 
2 26 Feb 07 jari 325                 //    System.out.println("children length = "+children.length);
2 26 Feb 07 jari 326                 start = currNode.getBottomAnchorPoint();
2 26 Feb 07 jari 327                 
2 26 Feb 07 jari 328                 //     System.out.println("start x, y = "+start.x + "  " + start.y);
2 26 Feb 07 jari 329                 for(int k = 0; k < children.length; k++) {
2 26 Feb 07 jari 330                     
2 26 Feb 07 jari 331                     selected = false;
2 26 Feb 07 jari 332                     
2 26 Feb 07 jari 333                     if(this.selectedPathNodes.contains(currNode) && this.selectedPathNodes.contains(children[k])) {
2 26 Feb 07 jari 334                         selected = true;
2 26 Feb 07 jari 335                         g.setColor(Color.blue);
2 26 Feb 07 jari 336                     } else if(this.selectedPathNodes.size() != 0) {  //not in path but a path does exist
2 26 Feb 07 jari 337                                                                     //set alpha value
2 26 Feb 07 jari 338                         AlphaComposite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
2 26 Feb 07 jari 339                         g2.setComposite(alphaComp);
2 26 Feb 07 jari 340                     }
2 26 Feb 07 jari 341                     
2 26 Feb 07 jari 342                     //    System.out.println("draw line");
2 26 Feb 07 jari 343                     finish = children[k].getTopAnchorPoint();
2 26 Feb 07 jari 344                     //    System.out.println("finish x, y = "+finish.x + "  " + finish.y);
2 26 Feb 07 jari 345                     
2 26 Feb 07 jari 346                     if(!isStraitConnector) {
2 26 Feb 07 jari 347                         CubicCurve2D conn = new CubicCurve2D.Double(start.x, start.y, start.x, start.y +15,
2 26 Feb 07 jari 348                         finish.x, finish.y-15, finish.x, finish.y);               
2 26 Feb 07 jari 349                         g2.draw(conn);
2 26 Feb 07 jari 350                         if(selected) {
2 26 Feb 07 jari 351                             conn = new CubicCurve2D.Double(start.x+1, start.y, start.x+1, start.y+16,
2 26 Feb 07 jari 352                             finish.x+1, finish.y-16, finish.x+1, finish.y);
2 26 Feb 07 jari 353                             g2.draw(conn);
2 26 Feb 07 jari 354                         }
2 26 Feb 07 jari 355                     } else {
2 26 Feb 07 jari 356                         g.drawLine(start.x, start.y, finish.x, finish.y);
2 26 Feb 07 jari 357                         if(selected)
2 26 Feb 07 jari 358                             g.drawLine(start.x+1, start.y, finish.x+1, finish.y);
2 26 Feb 07 jari 359                     }
2 26 Feb 07 jari 360                     //reset color if changed
2 26 Feb 07 jari 361                     if(selected)
2 26 Feb 07 jari 362                         g.setColor(origColor);
2 26 Feb 07 jari 363                     g2.setComposite(composite);
2 26 Feb 07 jari 364                 }
2 26 Feb 07 jari 365             }
2 26 Feb 07 jari 366         }
2 26 Feb 07 jari 367     }
2 26 Feb 07 jari 368     
2 26 Feb 07 jari 369     public static void main(String [] args) {
2 26 Feb 07 jari 370         
2 26 Feb 07 jari 371     }
2 26 Feb 07 jari 372     
2 26 Feb 07 jari 373 }