Received: from SOUTH-STATION-ANNEX.MIT.EDU by po10.MIT.EDU (5.61/4.7) id AA05641; Tue, 15 Feb 00 17:15:53 EST
Received: from hermes.javasoft.com by MIT.EDU with SMTP
	id AA10014; Tue, 15 Feb 00 17:15:24 EST
Received: (from nobody@localhost)
	by hermes.java.sun.com (8.9.3+Sun/8.9.1) id WAA23009;
	Tue, 15 Feb 2000 22:16:19 GMT
Date: Tue, 15 Feb 2000 22:16:19 GMT
Message-Id: <200002152216.WAA23009@hermes.java.sun.com>
X-Authentication-Warning: hermes.java.sun.com: Processed from queue /bulkmail/data/ed_26/mqueue5
X-Mailing: 195
From: JDCTechTips@sun.com
Subject: JDC Tech Tips  February 15, 2000
To: JDCMember@sun.com
Reply-To: JDCTechTips@sun.com
Errors-To: bounced_mail@hermes.java.sun.com
Precedence: junk
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
X-Mailer: Beyond Email 2.2


 J  D  C    T  E  C  H    T  I  P  S

                      TIPS, TECHNIQUES, AND SAMPLE CODE


WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, 
February 15, 2000. This issue covers:

         * Manipulating Hierarchical Data with JTree
         * Invoking Programs from Java(tm) Applications
         
These tips were developed using Java(tm) 2 SDK, Standard Edition, 
v 1.2.2.

You can view this issue of the Tech Tips on the Web at 
http://developer.java.sun.com/developer/TechTips/2000/tt0209.html.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

MANIPULATING HIERARCHICAL DATA WITH JTREE

JTree is a Swing component used to manipulate hierarchical data 
such as directory/file trees. If you've worked with a file browser 
of any type you've probably used a tree component. You can collapse
and expand the various nodes in the hierarchy. This tip will
cover some basics in using JTree.

A tree component consists of a root node and a set of child nodes. 
Each node contains a user object (like a string) and zero or more
child nodes. For example, you might have a tree structure like
this:

    testing
        one
            1.1
        two
            2.1
        three
            3.1
            3.2
            3.3

The root node, testing, has three children. These child nodes have
children as well. A node with no children, such as 3.2, is a leaf
node.

Nodes are represented by the DefaultMutableTreeNode class, which 
implements the interfaces TreeNode and MutableTreeNode. 
Mutable means that the node can change, by adding or deleting
children, or by changing the user object.

Here is a simple example of using JTree:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import javax.swing.tree.*;
    import java.util.Vector;

    public class JTreeDemo {
        public static void main(String args[]) {
            JFrame frame = new JFrame("JTree Demo");

            // handle window close

            frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });

            JPanel panel1 = new JPanel();

            // set up tree root and nodes

            DefaultMutableTreeNode root =
                new DefaultMutableTreeNode("testing");

            DefaultMutableTreeNode one =
                new DefaultMutableTreeNode("one");
            one.add(new DefaultMutableTreeNode("1.1"));
            one.add(new DefaultMutableTreeNode("1.2"));

            DefaultMutableTreeNode two =
                new DefaultMutableTreeNode("two");
            two.add(new DefaultMutableTreeNode("2.1"));
            two.add(new DefaultMutableTreeNode("2.2"));

            DefaultMutableTreeNode three =
                new DefaultMutableTreeNode("three");
            Vector vec = new Vector();
            for (int i = 1; i <= 25; i++)
                vec.addElement("3." + i);
            JTree.DynamicUtilTreeNode.createChildren(three, vec);

            root.add(one);
            root.add(two);
            root.add(three);

            // set up tree and scroller for it
            // also set text selection color to red

            JTree jt = new JTree(root);
            DefaultTreeCellRenderer tcr =
                (DefaultTreeCellRenderer)jt.getCellRenderer();
            tcr.setTextSelectionColor(Color.red);

            JScrollPane jsp = new JScrollPane(jt);
            jsp.setPreferredSize(new Dimension(200, 300));

            // set text field for echoing selections

            JPanel panel2 = new JPanel();
            final JTextField tf = new JTextField(25);
            panel2.add(tf);

            // handle selections in the tree

            TreeSelectionListener listen;
            listen = new TreeSelectionListener() {
                public void valueChanged(TreeSelectionEvent e) {

                    // get selected path

                    TreePath path = e.getPath();
                    int cnt = path.getPathCount();
                    StringBuffer sb = new StringBuffer();

                    // pick out the path components

                    for (int i = 0; i < cnt; i++) {
                        String s =
                            path.getPathComponent(i).toString();
                        sb.append(s);
                        if (i + 1 != cnt)
                            sb.append("#");
                    }
                    tf.setText(sb.toString());
                }
            };
            jt.addTreeSelectionListener(listen);

            panel1.add(jsp);

            frame.getContentPane().add("North", panel1);
            frame.getContentPane().add("South", panel2);
            frame.setLocation(100, 100);
            frame.pack();
            frame.setVisible(true);
        }
    }

The node structure for the tree is constructed in a straightforward
way. As nodes are created: 

            DefaultMutableTreeNode one =
                new DefaultMutableTreeNode("one");
            one.add(new DefaultMutableTreeNode("1.1"));
            one.add(new DefaultMutableTreeNode("1.2"));

they are added to the parent node:

           root.add(one);

Creating nodes one at a time, however, is tedious for large
trees. So the demo illustrates an alternative. This approach 
uses the createChildren method of the JTree.DynamicUtilTreeNode 
class to create a series of nodes from a Vector object.

           DefaultMutableTreeNode three =
               new DefaultMutableTreeNode("three");
           Vector vec = new Vector();
           for (int i = 1; i <= 25; i++)
               vec.addElement("3." + i);
           JTree.DynamicUtilTreeNode.createChildren(three, vec);
            
In this case, it adds 25 children to the "three" node.

Once a tree is set up and displayed, how do you handle node 
selection in the tree? The demo shows how to set up a tree 
selection listener, and get and display a path. The path is 
a sequence of nodes from the root to the currently-selected node 
in the tree. A path might be:

    testing#three#3.7
    
Notice that when you select a node in the tree the path is
displayed in the lower text box.    

There are many other aspects of JTree. For example, the class 
DefaultTreeCellRenderer allows you to control the way nodes are 
displayed. The demo above uses cell renderers, in a basic way;
it specifies that the current selection should be displayed 
in red. But there's more that you can do with this class. For 
example, you can use it to specify an icon to be displayed
when nodes are drawn.

INVOKING PROGRAMS FROM JAVA APPLICATIONS

The December 14, 1999 issue of the Tech Tips, see
(http://developer.java.sun.com/developer/TechTips/1999/tt1214.html)
discussed how RMI (Remote Method Invocation) can be used to
communicate between programs. Another technique for communication 
is the Runtime.exec method. You can use this method to invoke
a program from within a running Java application. Runtime.exec 
also allows you to perform operations related to the program, such 
as control the program's standard input and output, wait until it 
completes execution, and get its exit status. Here's a simple C 
application that illustrates these features:

    #include <stdio.h>

    int main() {
        printf("testing\n");
        return 0;
    }

This application writes a string "testing" to standard output, and
then terminates with an exit status of 0.

To execute this simple program within a Java application, compile
the C application:

    $ cc test.c -o test
    
(your C compiler might require different parameters) and then 
invoke the program using this Java code:

    import java.io.*;
    import java.util.ArrayList;
    
    public class ExecDemo {
        static public String[] runCommand(String cmd)
            throws IOException {
    
            // set up list to capture command output lines
    
            ArrayList list = new ArrayList();
    
            // start command running
   
            Process proc = Runtime.getRuntime().exec(cmd);
    
            // get command's output stream and
            // put a buffered reader input stream on it
    
            InputStream istr = proc.getInputStream();
            BufferedReader br =
                new BufferedReader(new InputStreamReader(istr));
    
            // read output lines from command
    
            String str;
            while ((str = br.readLine()) != null)
                list.add(str);
    
            // wait for command to terminate
    
            try {
                proc.waitFor();
            }
            catch (InterruptedException e) {
                System.err.println("process was interrupted");
            }
    
            // check its exit value
    
            if (proc.exitValue() != 0)
                System.err.println("exit value was non-zero");
    
            // close stream
    
            br.close();
    
            // return list of strings to caller
    
            return (String[])list.toArray(new String[0]);
        }
    
        public static void main(String args[]) throws IOException {
            try {
    
                // run a command
    
                String outlist[] = runCommand("test");
    
                // display its output
    
                for (int i = 0; i < outlist.length; i++)
                    System.out.println(outlist[i]);
            }
            catch (IOException e) {
                System.err.println(e);
            }
        }
    }

The demo calls a method runCommand to actually run the program.

     String outlist[] = runCommand("test");
               
This method hooks an input stream to the program's output stream, 
so that it can read the program's output, and save it into a list 
of strings. 

     InputStream istr = proc.getInputStream();
     BufferedReader br =
         new BufferedReader(new InputStreamReader(istr));  
                
     String str;
     while ((str = br.readLine()) != null)
         list.add(str);
    
After all the output has been read, waitFor is called to
wait on the program to terminate, and then exitValue is called to
get the exit value of the program. If you've done much systems
programming, for example with UNIX system calls, this approach 
will be a familiar one. (This example assumes that the current 
directory is in your shell search path; more on this subject 
below).

If you're on a UNIX system, you can replace:

    runCommand("test");

with:

    runCommand("ls -l");

to get a full (long) listing of files in the current directory. 
But getting a listing in this way highlights a fundamental 
weakness of using Runtime.exec -- the programs you invoke aren't 
necessarily portable. That is, Runtime.exec is portable, and 
exists across different Java implementations, but the invoked 
programs are not. There's no program named "ls" on Windows 
systems.

Suppose that you're running Windows NT and you decide to remedy 
this problem by saying:

    runCommand("dir");

where "dir" is the equivalent command to "ls". This doesn't work,
because "dir" is not an executable program. Instead it is
built into the shell (command interpreter) CMD.EXE. So you need 
to say:

    runCommand("cmd /c dir");

where "cmd /c command" says "invoke a shell and execute the single
specified command and then exit." Similarly, for a UNIX shell like
the Korn shell, you might say:

    runCommand("ksh -c alias");

where "alias" is a command built into the shell. The output in this
case is a list of all your shell aliases.

In the example above of obtaining a directory listing, you can use
portable Java facilities to achieve the same result. For example,
saying:

    import java.io.File;

    public class DumpFiles {
        public static void main(String args[]) {
            String list[] = new File(".").list();
            for (int i = 0; i < list.length; i++)
                System.out.println(list[i]);
        }
    }

gives you a list of all files and directories in the
current directory. So using ls/dir probably doesn't make sense in
most cases.

A situation where it makes sense to use Runtime.exec is one
in which you allow the user to specify an editor or word processor
(like Emacs or Vi or Word) to edit files. This is a common feature
in large applications. The application would have a configuration
file with the local path of the editor, and Runtime.exec would be
called with this path.

One tricky aspect of Runtime.exec is how it finds files. For
example, if you say:

    Runtime.exec("ls");

how is the "ls" program found? Experiments with JDK 1.2.2 indicate
that the PATH environment variable is searched. This is just like 
what happens when you execute commands with a shell. But the 
documentation doesn't address this point, so it pays to be careful. 
You can't assume that a search path has been set. It might make 
more sense to use Runtime.exec in a limited way as discussed above, 
with absolute paths specified.

There's also a variant of Runtime.exec that allows you to specify
environment strings.

.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .

- NOTE
The names on the JDC mailing list are used for internal Sun
Microsystems(tm) purposes only. To remove your name from the list,
see Subscribe/Unsubscribe below.


- FEEDBACK
Comments? Send your feedback on the JDC Tech Tips to:

jdc-webmaster@sun.com


- SUBSCRIBE/UNSUBSCRIBE
The JDC Tech Tips are sent to you because you elected to subscribe
when you registered as a JDC member. To unsubscribe from JDC email,
go to the following address and enter the email address you wish to
remove from the mailing list:

http://developer.java.sun.com/unsubscribe.html


To become a JDC member and subscribe to this newsletter go to:

http://java.sun.com/jdc/


- ARCHIVES
You'll find the JDC Tech Tips archives at:

http://developer.java.sun.com/developer/TechTips/index.html


- COPYRIGHT
Copyright 2000 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright. For more information, see:

http://developer.java.sun.com/developer/copyright.html


This issue of the JDC Tech Tips is written by Glen McCluskey.

JDC Tech Tips 
February 15, 2000












