[pkg-java] r3438 - in trunk/rhino/debian: . patches src_from_treetable_article_see_bug409629

Arnaud Vandyck avdyk at alioth.debian.org
Fri May 11 12:22:02 UTC 2007


Author: avdyk
Date: 2007-05-11 12:22:01 +0000 (Fri, 11 May 2007)
New Revision: 3438

Added:
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java
   trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java
Removed:
   trunk/rhino/debian/patches/02-dont_build-toolsrc.patch
Modified:
   trunk/rhino/debian/changelog
Log:
rhino (1.6.R5-2) unstable; urgency=low

  * added java classes from a tutorial from Sun (BSD license as mentionned
    in #409629), thanks to Roktas, Filipe Lautert and Bruno Harbulot for
    filing bug, giving infos and proposed a patch (even if it's not
    used). I've just removed the 02-dont_build-toolsrc.patch. I added the
    classes examples if needed but it seems not to be the case (closes:
    #409629).

 -- Arnaud Vandyck <avdyk at debian.org>  Fri, 11 May 2007 14:07:21 +0200

Modified: trunk/rhino/debian/changelog
===================================================================
--- trunk/rhino/debian/changelog	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/changelog	2007-05-11 12:22:01 UTC (rev 3438)
@@ -1,3 +1,14 @@
+rhino (1.6.R5-2) unstable; urgency=low
+
+  * added java classes from a tutorial from Sun (BSD license as mentionned
+    in #409629), thanks to Roktas, Filipe Lautert and Bruno Harbulot for
+    filing bug, giving infos and proposed a patch (even if it's not
+    used). I've just removed the 02-dont_build-toolsrc.patch. I added the
+    classes examples if needed but it seems not to be the case (closes:
+    #409629).
+
+ -- Arnaud Vandyck <avdyk at debian.org>  Fri, 11 May 2007 14:07:21 +0200
+
 rhino (1.6.R5-1) unstable; urgency=low
 
   * New upstream maintenance release.

Deleted: trunk/rhino/debian/patches/02-dont_build-toolsrc.patch
===================================================================
--- trunk/rhino/debian/patches/02-dont_build-toolsrc.patch	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/patches/02-dont_build-toolsrc.patch	2007-05-11 12:22:01 UTC (rev 3438)
@@ -1,13 +0,0 @@
---- build.xml~	2005-09-19 07:20:18.000000000 +0000
-+++ build.xml	2006-11-06 11:44:43.000000000 +0000
-@@ -34,8 +34,8 @@
- 
-   <target name="compile" depends="init">
-     <ant antfile="src/build.xml" target="compile"/>
--    <ant antfile="toolsrc/build.xml" target="compile"/>
--    <antcall target="xmlimplsrc-compile" />
-+    <!-- ant antfile="toolsrc/build.xml" target="compile"/ -->
-+    <!-- antcall target="xmlimplsrc-compile" / -->
-   </target>
- 
-   <target name="compile-all" depends="compile">

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,60 @@
+import java.awt.Component;
+import java.awt.event.*;
+import java.awt.AWTEvent;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.EventObject;
+import java.io.Serializable;
+
+public class AbstractCellEditor implements CellEditor {
+
+    protected EventListenerList listenerList = new EventListenerList();
+
+    public Object getCellEditorValue() { return null; }
+    public boolean isCellEditable(EventObject e) { return true; }
+    public boolean shouldSelectCell(EventObject anEvent) { return false; }
+    public boolean stopCellEditing() { return true; }
+    public void cancelCellEditing() {}
+
+    public void addCellEditorListener(CellEditorListener l) {
+	listenerList.add(CellEditorListener.class, l);
+    }
+
+    public void removeCellEditorListener(CellEditorListener l) {
+	listenerList.remove(CellEditorListener.class, l);
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  
+     * @see EventListenerList
+     */
+    protected void fireEditingStopped() {
+	// Guaranteed to return a non-null array
+	Object[] listeners = listenerList.getListenerList();
+	// Process the listeners last to first, notifying
+	// those that are interested in this event
+	for (int i = listeners.length-2; i>=0; i-=2) {
+	    if (listeners[i]==CellEditorListener.class) {
+		((CellEditorListener)listeners[i+1]).editingStopped(new ChangeEvent(this));
+	    }	       
+	}
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  
+     * @see EventListenerList
+     */
+    protected void fireEditingCanceled() {
+	// Guaranteed to return a non-null array
+	Object[] listeners = listenerList.getListenerList();
+	// Process the listeners last to first, notifying
+	// those that are interested in this event
+	for (int i = listeners.length-2; i>=0; i-=2) {
+	    if (listeners[i]==CellEditorListener.class) {
+		((CellEditorListener)listeners[i+1]).editingCanceled(new ChangeEvent(this));
+	    }	       
+	}
+    }
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,195 @@
+/*
+ * @(#)AbstractTreeTableModel.java	1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+import javax.swing.tree.*;
+import javax.swing.event.*;
+ 
+/**
+ * @version 1.2 10/27/98
+ * An abstract implementation of the TreeTableModel interface, handling the list 
+ * of listeners. 
+ * @author Philip Milne
+ */
+
+public abstract class AbstractTreeTableModel implements TreeTableModel {
+    protected Object root;     
+    protected EventListenerList listenerList = new EventListenerList();
+  
+    public AbstractTreeTableModel(Object root) {
+        this.root = root; 
+    }
+
+    //
+    // Default implmentations for methods in the TreeModel interface. 
+    //
+
+    public Object getRoot() {
+        return root;
+    }
+
+    public boolean isLeaf(Object node) {
+        return getChildCount(node) == 0; 
+    }
+
+    public void valueForPathChanged(TreePath path, Object newValue) {}
+
+    // This is not called in the JTree's default mode: use a naive implementation. 
+    public int getIndexOfChild(Object parent, Object child) {
+        for (int i = 0; i < getChildCount(parent); i++) {
+	    if (getChild(parent, i).equals(child)) { 
+	        return i; 
+	    }
+        }
+	return -1; 
+    }
+
+    public void addTreeModelListener(TreeModelListener l) {
+        listenerList.add(TreeModelListener.class, l);
+    }
+
+    public void removeTreeModelListener(TreeModelListener l) {
+        listenerList.remove(TreeModelListener.class, l);
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeNodesChanged(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
+            }          
+        }
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeNodesInserted(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
+            }          
+        }
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeNodesRemoved(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
+            }          
+        }
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeStructureChanged(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
+            }          
+        }
+    }
+
+    //
+    // Default impelmentations for methods in the TreeTableModel interface. 
+    //
+
+    public Class getColumnClass(int column) { return Object.class; }
+
+   /** By default, make the column with the Tree in it the only editable one. 
+    *  Making this column editable causes the JTable to forward mouse 
+    *  and keyboard events in the Tree column to the underlying JTree. 
+    */ 
+    public boolean isCellEditable(Object node, int column) { 
+         return getColumnClass(column) == TreeTableModel.class; 
+    }
+
+    public void setValueAt(Object aValue, Object node, int column) {}
+
+
+    // Left to be implemented in the subclass:
+
+    /* 
+     *   public Object getChild(Object parent, int index)
+     *   public int getChildCount(Object parent) 
+     *   public int getColumnCount() 
+     *   public String getColumnName(Object node, int column)  
+     *   public Object getValueAt(Object node, int column) 
+     */
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,774 @@
+/*
+ * FileSystemModel2.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import java.io.IOException;
+import java.io.File;
+import java.util.Date;
+import java.util.Stack;
+import javax.swing.SwingUtilities;
+import javax.swing.tree.TreePath;
+
+/**
+ * FileSystemModel2 is a TreeTableModel representing a hierarchical file
+ * system.<p>
+ * This will recursively load all the children from the path it is
+ * created with. The loading is done with the method reloadChildren, and
+ * happens in another thread. The method isReloading can be invoked to check
+ * if there are active threads. The total size of all the files are also
+ * accumulated.
+ * <p>
+ * By default links are not descended. java.io.File does not have a way
+ * to distinguish links, so a file is assumed to be a link if its canonical
+ * path does not start with its parent path. This may not cover all cases,
+ * but works for the time being.
+ * <p>Reloading happens such that all the files of the directory are
+ * loaded and immediately available. The background thread then recursively
+ * loads all the files of each of those directories. When each directory has
+ * finished loading all its sub files they are attached and an event is
+ * generated in the event dispatching thread. A more ambitious approach
+ * would be to attach each set of directories as they are loaded and generate
+ * an event. Then, once all the direct descendants of the directory being
+ * reloaded have finished loading, it is resorted based on total size.
+ * <p>
+ * While you can invoke reloadChildren as many times as you want, care
+ * should be taken in doing this. You should not invoke reloadChildren
+ * on a node that is already being reloaded, or going to be reloaded
+ * (meaning its parent is reloading but it hasn't started reloading
+ * that directory yet). If this is done odd results may
+ * happen. FileSystemModel2 does not enforce any policy in this manner,
+ * and it is up to the user of FileSystemModel2 to ensure it doesn't
+ * happen.
+ * 
+ * @version 1.12 05/12/98
+ * @author Philip Milne
+ * @author Scott Violet
+ */
+
+public class FileSystemModel2 extends AbstractTreeTableModel {
+
+    // Names of the columns.
+    static protected String[]  cNames = {"Name", "Size", "Type", "Modified"};
+
+    // Types of the columns.
+    static protected Class[]  cTypes = { TreeTableModel.class,
+					 Integer.class, String.class,
+					 Date.class};
+
+    // The the returned file length for directories. 
+    public static final Integer      ZERO = new Integer(0); 
+
+    /** An array of MergeSorter sorters, that will sort based on size. */
+    static Stack      sorters = new Stack();
+
+
+    /** True if the receiver is valid, once set to false all Threads
+     * loading files will stop. */
+    protected boolean                isValid;
+
+    /** Node currently being reloaded, this becomes somewhat muddy if
+     * reloading is happening in multiple threads. */
+    protected FileNode               reloadNode;
+
+    /** > 0 indicates reloading some nodes. */
+    int                              reloadCount;
+
+    /** Returns true if links are to be descended. */
+    protected boolean                descendLinks;
+
+
+    /**
+     * Returns a MergeSort that can sort on the totalSize of a FileNode.
+     */
+    protected static MergeSort getSizeSorter() {
+	synchronized(sorters) {
+	    if (sorters.size() == 0) {
+		return new SizeSorter();
+	    }
+	    return (MergeSort)sorters.pop();
+	}
+    }
+
+    /**
+     * Should be invoked when a MergeSort is no longer needed.
+     */
+    protected static void recycleSorter(MergeSort sorter) {
+	synchronized(sorters) {
+	    sorters.push(sorter);
+	}
+    }
+
+
+    /**
+     * Creates a FileSystemModel2 rooted at File.separator, which is usually
+     * the root of the file system. This does not load it, you should invoke
+     * <code>reloadChildren</code> with the root to start loading.
+     */
+    public FileSystemModel2() { 
+	this(File.separator);
+    }
+
+    /**
+     * Creates a FileSystemModel2 with the root being <code>rootPath</code>.
+     * This does not load it, you should invoke
+     * <code>reloadChildren</code> with the root to start loading.
+     */
+    public FileSystemModel2(String rootPath) { 
+	super(null);
+	isValid = true;
+	root = new FileNode(new File(rootPath));
+    }
+
+    //
+    // The TreeModel interface
+    //
+
+    /**
+     * Returns the number of children of <code>node</code>.
+     */
+    public int getChildCount(Object node) { 
+	Object[] children = getChildren(node); 
+	return (children == null) ? 0 : children.length;
+    }
+
+    /**
+     * Returns the child of <code>node</code> at index <code>i</code>.
+     */
+    public Object getChild(Object node, int i) { 
+	return getChildren(node)[i]; 
+    }
+
+    /**
+     * Returns true if the passed in object represents a leaf, false
+     * otherwise.
+     */
+    public boolean isLeaf(Object node) {
+	return ((FileNode)node).isLeaf();
+    }
+
+    //
+    //  The TreeTableNode interface. 
+    //
+
+    /**
+     * Returns the number of columns.
+     */
+    public int getColumnCount() {
+	return cNames.length;
+    }
+
+    /**
+     * Returns the name for a particular column.
+     */
+    public String getColumnName(int column) {
+	return cNames[column];
+    }
+
+    /**
+     * Returns the class for the particular column.
+     */
+    public Class getColumnClass(int column) {
+	return cTypes[column];
+    }
+
+    /**
+     * Returns the value of the particular column.
+     */
+    public Object getValueAt(Object node, int column) {
+	FileNode     fn = (FileNode)node;
+
+	try {
+	    switch(column) {
+	    case 0:
+		return fn.getFile().getName();
+	    case 1:
+		if (fn.isTotalSizeValid()) {
+		    return new Integer((int)((FileNode)node).totalSize());
+		}
+		return null;
+	    case 2:
+		return fn.isLeaf() ?  "File" : "Directory";
+	    case 3:
+		return fn.lastModified();
+	    }
+	}
+	catch  (SecurityException se) { }
+   
+	return null; 
+    }
+
+    //
+    // Some convenience methods. 
+    //
+
+    /**
+     * Reloads the children of the specified node.
+     */
+    public void reloadChildren(Object node) {
+	FileNode         fn = (FileNode)node;
+
+	synchronized(this) {
+	    reloadCount++;
+	}
+	fn.resetSize();
+	new Thread(new FileNodeLoader((FileNode)node)).start();
+    }
+
+    /**
+     * Stops and waits for all threads to finish loading.
+     */
+    public void stopLoading() {
+	isValid = false;
+	synchronized(this) {
+	    while (reloadCount > 0) {
+		try {
+		    wait();
+		} catch (InterruptedException ie) {}
+	    }
+	}
+	isValid = true;
+    }
+
+    /**
+     * If <code>newValue</code> is true, links are descended. Odd results
+     * may happen if you set this while other threads are loading.
+     */
+    public void setDescendsLinks(boolean newValue) {
+	descendLinks = newValue;
+    }
+
+    /**
+     * Returns true if links are to be automatically descended.
+     */
+    public boolean getDescendsLinks() {
+	return descendLinks;
+    }
+
+    /**
+     * Returns the path <code>node</code> represents.
+     */
+    public String getPath(Object node) {
+	return ((FileNode)node).getFile().getPath();
+    }
+
+    /**
+     * Returns the total size of the receiver.
+     */
+    public long getTotalSize(Object node) {
+	return ((FileNode)node).totalSize();
+    }
+
+    /**
+     * Returns true if the receiver is loading any children.
+     */
+    public boolean isReloading() {
+	return (reloadCount > 0);
+    }
+
+    /**
+     * Returns the path to the node that is being loaded.
+     */
+    public TreePath getPathLoading() {
+	FileNode      rn = reloadNode;
+
+	if (rn != null) {
+	    return new TreePath(rn.getPath());
+	}
+	return null;
+    }
+
+    /**
+     * Returns the node being loaded.
+     */
+    public Object getNodeLoading() {
+	return reloadNode;
+    }
+
+    protected File getFile(Object node) {
+	FileNode fileNode = ((FileNode)node); 
+	return fileNode.getFile();       
+    }
+
+    protected Object[] getChildren(Object node) {
+	FileNode fileNode = ((FileNode)node); 
+	return fileNode.getChildren(); 
+    }
+
+
+    protected static FileNode[] EMPTY_CHILDREN = new FileNode[0];
+
+    // Used to sort the file names.
+    static private MergeSort  fileMS = new MergeSort() {
+	public int compareElementsAt(int beginLoc, int endLoc) {
+	    return ((String)toSort[beginLoc]).compareTo
+		            ((String)toSort[endLoc]);
+	}
+    };
+
+
+    /**
+     * A FileNode is a derivative of the File class - though we delegate to
+     * the File object rather than subclassing it. It is used to maintain a
+     * cache of a directory's children and therefore avoid repeated access
+     * to the underlying file system during rendering. 
+     */
+    class FileNode { 
+	/** java.io.File the receiver represents. */
+	protected File              file; 
+	/** Parent FileNode of the receiver. */
+	private FileNode            parent;
+	/** Children of the receiver. */
+	protected FileNode[]        children; 
+	/** Size of the receiver and all its children. */
+	protected long              totalSize;
+	/** Valid if the totalSize has finished being calced. */
+	protected boolean           totalSizeValid;
+	/** Path of the receiver. */
+	protected String            canonicalPath;
+	/** True if the canonicalPath of this instance does not start with
+	 * the canonical path of the parent. */
+	protected boolean           isLink;
+	/** Date last modified. */
+	protected Date              lastModified;
+
+
+	protected FileNode(File file) { 
+	    this(null, file);
+	}
+
+	protected FileNode(FileNode parent, File file) { 
+	    this.parent = parent;
+	    this.file = file;
+	    try {
+		canonicalPath = file.getCanonicalPath();
+	    }
+	    catch (IOException ioe) {
+		canonicalPath = "";
+	    }
+	    if (parent != null) {
+		isLink = !canonicalPath.startsWith(parent.getCanonicalPath());
+	    }
+	    else {
+		isLink = false;
+	    }
+	    if (isLeaf()) {
+		totalSize = file.length();
+		totalSizeValid = true;
+	    }
+	}
+
+	/**
+	 * Returns the date the receiver was last modified.
+	 */
+	public Date lastModified() {
+	    if (lastModified == null && file != null) {
+		lastModified = new Date(file.lastModified());
+	    }
+	    return lastModified;
+	}
+
+	/**
+	 * Returns the the string to be used to display this leaf in the JTree.
+	 */
+	public String toString() { 
+	    return file.getName();
+	}
+
+	/**
+	 * Returns the java.io.File the receiver represents.
+	 */
+	public File getFile() {
+	    return file; 
+	}
+
+	/**
+	 * Returns size of the receiver and all its children.
+	 */
+	public long totalSize() {
+	    return totalSize;
+	}
+
+	/**
+	 * Returns the parent of the receiver.
+	 */
+	public FileNode getParent() {
+	    return parent;
+	}
+
+	/**
+	 * Returns true if the receiver represents a leaf, that is it is
+	 * isn't a directory.
+	 */
+	public boolean isLeaf() {
+	    return file.isFile();
+	}
+
+	/**
+	 * Returns true if the total size is valid.
+	 */
+	public boolean isTotalSizeValid() {
+	    return totalSizeValid;
+	}
+
+	/**
+	 * Clears the date.
+	 */
+	protected void resetLastModified() {
+	    lastModified = null;
+	}
+
+	/**
+	 * Sets the size of the receiver to be 0.
+	 */
+	protected void resetSize() {
+	    alterTotalSize(-totalSize);
+	}
+
+	/**
+	 * Loads the children, caching the results in the children
+	 * instance variable.
+	 */
+	protected FileNode[] getChildren() {
+	    return children; 
+	}
+
+	/**
+	 * Recursively loads all the children of the receiver.
+	 */
+	protected void loadChildren(MergeSort sorter) {
+	    totalSize = file.length();
+	    children = createChildren(null);
+	    for (int counter = children.length - 1; counter >= 0; counter--) {
+		Thread.yield(); // Give the GUI CPU time to draw itself.
+		if (!children[counter].isLeaf() &&
+		    (descendLinks || !children[counter].isLink())) {
+		    children[counter].loadChildren(sorter);
+		}
+		totalSize += children[counter].totalSize();
+		if (!isValid) {
+		    counter = 0;
+		}
+	    }
+	    if (isValid) {
+		if (sorter != null) {
+		    sorter.sort(children);
+		}
+		totalSizeValid = true;
+	    }
+	}
+
+	/**
+	 * Loads the children of of the receiver.
+	 */
+	protected FileNode[] createChildren(MergeSort sorter) {
+	    FileNode[]        retArray = null;
+
+	    try {
+		String[] files = file.list();
+		if(files != null) {
+		    if (sorter != null) {
+			sorter.sort(files); 
+		    }
+		    retArray = new FileNode[files.length]; 
+		    String path = file.getPath();
+		    for(int i = 0; i < files.length; i++) {
+			File childFile = new File(path, files[i]); 
+			retArray[i] = new FileNode(this, childFile);
+		    }
+		}
+	    } catch (SecurityException se) {}
+	    if (retArray == null) {
+		retArray = EMPTY_CHILDREN;
+	    }
+	    return retArray;
+	}
+
+	/**
+	 * Returns true if the children have been loaded.
+	 */
+	protected boolean loadedChildren() {
+	    return (file.isFile() || (children != null));
+	}
+
+	/**
+	 * Gets the path from the root to the receiver.
+	 */
+	public FileNode[] getPath() {
+	    return getPathToRoot(this, 0);
+	}
+
+	/**
+	 * Returns the canonical path for the receiver.
+	 */
+	public String getCanonicalPath() {
+	    return canonicalPath;
+	}
+
+	/**
+	 * Returns true if the receiver's path does not begin with the
+	 * parent's canonical path.
+	 */
+	public boolean isLink() {
+	    return isLink;
+	}
+
+	protected FileNode[] getPathToRoot(FileNode aNode, int depth) {
+	    FileNode[]              retNodes;
+
+	    if(aNode == null) {
+		if(depth == 0)
+		    return null;
+		else
+		    retNodes = new FileNode[depth];
+	    }
+	    else {
+		depth++;
+		retNodes = getPathToRoot(aNode.getParent(), depth);
+		retNodes[retNodes.length - depth] = aNode;
+	    }
+	    return retNodes;
+	}
+
+	/**
+	 * Sets the children of the receiver, updates the total size,
+	 * and if generateEvent is true a tree structure changed event
+	 * is created.
+	 */
+	protected void setChildren(FileNode[] newChildren,
+				   boolean generateEvent) {
+	    long           oldSize = totalSize;
+
+	    totalSize = file.length();
+	    children = newChildren;
+	    for (int counter = children.length - 1; counter >= 0;
+		 counter--) {
+		totalSize += children[counter].totalSize();
+	    }
+
+	    if (generateEvent) {
+		FileNode[]   path = getPath();
+
+		fireTreeStructureChanged(FileSystemModel2.this, path, null,
+					 null);
+
+		FileNode             parent = getParent();
+
+		if (parent != null) {
+		    parent.alterTotalSize(totalSize - oldSize);
+		}
+	    }
+	}
+
+	protected synchronized void alterTotalSize(long sizeDelta) {
+	    if (sizeDelta != 0 && (parent = getParent()) != null) {
+		totalSize += sizeDelta;
+		nodeChanged();
+		parent.alterTotalSize(sizeDelta);
+	    }
+	    else {
+		// Need a way to specify the root.
+		totalSize += sizeDelta;
+	    }
+	}
+
+	/**
+	 * This should only be invoked on the event dispatching thread.
+	 */
+	protected synchronized void setTotalSizeValid(boolean newValue) {
+	    if (totalSizeValid != newValue) {
+		nodeChanged();
+		totalSizeValid = newValue;
+
+		FileNode          parent = getParent();
+
+		if (parent != null) {
+		    parent.childTotalSizeChanged(this);
+		}
+	    }
+	}
+
+	/**
+	 * Marks the receivers total size as valid, but does not invoke
+	 * node changed, nor message the parent.
+	 */
+	protected synchronized void forceTotalSizeValid() {
+	    totalSizeValid = true;
+	}
+
+	/**
+	 * Invoked when a childs total size has changed.
+	 */
+	protected synchronized void childTotalSizeChanged(FileNode child) {
+	    if (totalSizeValid != child.isTotalSizeValid()) {
+		if (totalSizeValid) {
+		    setTotalSizeValid(false);
+		}
+		else {
+		    FileNode[]    children = getChildren();
+
+		    for (int counter = children.length - 1; counter >= 0;
+			 counter--) {
+			if (!children[counter].isTotalSizeValid()) {
+			    return;
+			}
+		    }
+		    setTotalSizeValid(true);
+		}
+	    }
+	    
+	}
+
+	/**
+	 * Can be invoked when a node has changed, will create the
+	 * appropriate event.
+	 */
+	protected void nodeChanged() {
+	    FileNode        parent = getParent();
+
+	    if (parent != null) {
+		FileNode[]   path = parent.getPath();
+		int[]        index = { getIndexOfChild(parent, this) };
+		Object[]     children = { this };
+
+		fireTreeNodesChanged(FileSystemModel2.this, path,  index,
+				     children);
+	    }
+	}
+    }
+
+
+    /**
+     * FileNodeLoader can be used to reload all the children of a
+     * particular node. It first resets the children of the FileNode
+     * it is created with, and in its run method will reload all of
+     * that nodes children. FileNodeLoader may not be running in the event
+     * dispatching thread. As swing is not thread safe it is important
+     * that we don't generate events in this thread. SwingUtilities.invokeLater
+     * is used so that events are generated in the event dispatching thread.
+     */
+    class FileNodeLoader implements Runnable {
+	/** Node creating children for. */
+	FileNode          node;
+	/** Sorter. */
+	MergeSort         sizeMS;
+
+	FileNodeLoader(FileNode node) {
+	    this.node = node;
+	    node.resetLastModified();
+	    node.setChildren(node.createChildren(fileMS), true);
+	    node.setTotalSizeValid(false);
+	}
+
+	public void run() {
+	    FileNode[]      children = node.getChildren();
+
+	    sizeMS = getSizeSorter();
+	    for (int counter = children.length - 1; counter >= 0; counter--) {
+		if (!children[counter].isLeaf()) {
+		    reloadNode = children[counter];
+		    loadChildren(children[counter]);
+		    reloadNode = null;
+		}
+		if (!isValid) {
+		    counter = 0;
+		}
+	    }
+	    recycleSorter(sizeMS);
+	    if (isValid) {
+		SwingUtilities.invokeLater(new Runnable() {
+		    public void run() {
+			MergeSort    sorter = getSizeSorter();
+
+			sorter.sort(node.getChildren());
+			recycleSorter(sorter);
+			node.setChildren(node.getChildren(), true);
+			synchronized(FileSystemModel2.this) {
+			    reloadCount--;
+			    FileSystemModel2.this.notifyAll();
+			}
+		    }
+		});
+	    }
+	    else {
+		synchronized(FileSystemModel2.this) {
+		    reloadCount--;
+		    FileSystemModel2.this.notifyAll();
+		}
+	    }
+	}
+
+	protected void loadChildren(FileNode node) {
+	    if (!node.isLeaf() && (descendLinks || !node.isLink())) {
+		final FileNode[]      children = node.createChildren(null);
+
+		for (int counter = children.length - 1; counter >= 0;
+		     counter--) {
+		    if (!children[counter].isLeaf()) {
+			if (descendLinks || !children[counter].isLink()) {
+			    children[counter].loadChildren(sizeMS);
+			}
+			else {
+			    children[counter].forceTotalSizeValid();
+			}
+		    }
+		    if (!isValid) {
+			counter = 0;
+		    }
+		}
+		if (isValid) {
+		    final FileNode       fn = node;
+
+		    // Reset the children 
+		    SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			    MergeSort    sorter = getSizeSorter();
+
+			    sorter.sort(children);
+			    recycleSorter(sorter);
+			    fn.setChildren(children, true);
+			    fn.setTotalSizeValid(true);
+			    fn.nodeChanged();
+			}
+		    });
+		}
+	    }
+	    else {
+		node.forceTotalSizeValid();
+	    }
+	}
+    }
+
+
+    /**
+     * Sorts the contents, which must be instances of FileNode based on
+     * totalSize.
+     */
+    static class SizeSorter extends MergeSort {
+	public int compareElementsAt(int beginLoc, int endLoc) {
+	    long    firstSize = ((FileNode)toSort[beginLoc]).totalSize();
+	    long    secondSize = ((FileNode)toSort[endLoc]).totalSize();
+
+	    if (firstSize != secondSize) {
+		return (int)(secondSize - firstSize);
+	    }
+	    return ((FileNode)toSort[beginLoc]).toString().compareTo
+		    (((FileNode)toSort[endLoc]).toString());
+	}
+    }
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,353 @@
+/*
+ * @(#)JTreeTable.java	1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import javax.swing.table.*;
+
+import java.awt.Dimension;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import java.awt.event.MouseEvent;
+
+import java.util.EventObject;
+
+/**
+ * This example shows how to create a simple JTreeTable component, 
+ * by using a JTree as a renderer (and editor) for the cells in a 
+ * particular column in the JTable.  
+ *
+ * @version 1.2 10/27/98
+ *
+ * @author Philip Milne
+ * @author Scott Violet
+ */
+public class JTreeTable extends JTable {
+    /** A subclass of JTree. */
+    protected TreeTableCellRenderer tree;
+
+    public JTreeTable(TreeTableModel treeTableModel) {
+	super();
+
+	// Create the tree. It will be used as a renderer and editor. 
+	tree = new TreeTableCellRenderer(treeTableModel);
+
+	// Install a tableModel representing the visible rows in the tree. 
+	super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
+
+	// Force the JTable and JTree to share their row selection models. 
+	ListToTreeSelectionModelWrapper selectionWrapper = new 
+	                        ListToTreeSelectionModelWrapper();
+	tree.setSelectionModel(selectionWrapper);
+	setSelectionModel(selectionWrapper.getListSelectionModel()); 
+
+	// Install the tree editor renderer and editor. 
+	setDefaultRenderer(TreeTableModel.class, tree); 
+	setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
+
+	// No grid.
+	setShowGrid(false);
+
+	// No intercell spacing
+	setIntercellSpacing(new Dimension(0, 0));	
+
+	// And update the height of the trees row to match that of
+	// the table.
+	if (tree.getRowHeight() < 1) {
+	    // Metal looks better like this.
+	    setRowHeight(18);
+	}
+    }
+
+    /**
+     * Overridden to message super and forward the method to the tree.
+     * Since the tree is not actually in the component hieachy it will
+     * never receive this unless we forward it in this manner.
+     */
+    public void updateUI() {
+	super.updateUI();
+	if(tree != null) {
+	    tree.updateUI();
+	}
+	// Use the tree's default foreground and background colors in the
+	// table. 
+        LookAndFeel.installColorsAndFont(this, "Tree.background",
+                                         "Tree.foreground", "Tree.font");
+    }
+
+    /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to 
+     * paint the editor. The UI currently uses different techniques to 
+     * paint the renderers and editors and overriding setBounds() below 
+     * is not the right thing to do for an editor. Returning -1 for the 
+     * editing row in this case, ensures the editor is never painted. 
+     */
+    public int getEditingRow() {
+        return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 :
+	        editingRow;  
+    }
+
+    /**
+     * Overridden to pass the new rowHeight to the tree.
+     */
+    public void setRowHeight(int rowHeight) { 
+        super.setRowHeight(rowHeight); 
+	if (tree != null && tree.getRowHeight() != rowHeight) {
+            tree.setRowHeight(getRowHeight()); 
+	}
+    }
+
+    /**
+     * Returns the tree that is being shared between the model.
+     */
+    public JTree getTree() {
+	return tree;
+    }
+
+    /**
+     * A TreeCellRenderer that displays a JTree.
+     */
+    public class TreeTableCellRenderer extends JTree implements
+	         TableCellRenderer {
+	/** Last table/tree row asked to renderer. */
+	protected int visibleRow;
+
+	public TreeTableCellRenderer(TreeModel model) {
+	    super(model); 
+	}
+
+	/**
+	 * updateUI is overridden to set the colors of the Tree's renderer
+	 * to match that of the table.
+	 */
+	public void updateUI() {
+	    super.updateUI();
+	    // Make the tree's cell renderer use the table's cell selection
+	    // colors. 
+	    TreeCellRenderer tcr = getCellRenderer();
+	    if (tcr instanceof DefaultTreeCellRenderer) {
+		DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); 
+		// For 1.1 uncomment this, 1.2 has a bug that will cause an
+		// exception to be thrown if the border selection color is
+		// null.
+		// dtcr.setBorderSelectionColor(null);
+		dtcr.setTextSelectionColor(UIManager.getColor
+					   ("Table.selectionForeground"));
+		dtcr.setBackgroundSelectionColor(UIManager.getColor
+						("Table.selectionBackground"));
+	    }
+	}
+
+	/**
+	 * Sets the row height of the tree, and forwards the row height to
+	 * the table.
+	 */
+	public void setRowHeight(int rowHeight) { 
+	    if (rowHeight > 0) {
+		super.setRowHeight(rowHeight); 
+		if (JTreeTable.this != null &&
+		    JTreeTable.this.getRowHeight() != rowHeight) {
+		    JTreeTable.this.setRowHeight(getRowHeight()); 
+		}
+	    }
+	}
+
+	/**
+	 * This is overridden to set the height to match that of the JTable.
+	 */
+	public void setBounds(int x, int y, int w, int h) {
+	    super.setBounds(x, 0, w, JTreeTable.this.getHeight());
+	}
+
+	/**
+	 * Sublcassed to translate the graphics such that the last visible
+	 * row will be drawn at 0,0.
+	 */
+	public void paint(Graphics g) {
+	    g.translate(0, -visibleRow * getRowHeight());
+	    super.paint(g);
+	}
+
+	/**
+	 * TreeCellRenderer method. Overridden to update the visible row.
+	 */
+	public Component getTableCellRendererComponent(JTable table,
+						       Object value,
+						       boolean isSelected,
+						       boolean hasFocus,
+						       int row, int column) {
+	    if(isSelected)
+		setBackground(table.getSelectionBackground());
+	    else
+		setBackground(table.getBackground());
+
+	    visibleRow = row;
+	    return this;
+	}
+    }
+
+
+    /**
+     * TreeTableCellEditor implementation. Component returned is the
+     * JTree.
+     */
+    public class TreeTableCellEditor extends AbstractCellEditor implements
+	         TableCellEditor {
+	public Component getTableCellEditorComponent(JTable table,
+						     Object value,
+						     boolean isSelected,
+						     int r, int c) {
+	    return tree;
+	}
+
+	/**
+	 * Overridden to return false, and if the event is a mouse event
+	 * it is forwarded to the tree.<p>
+	 * The behavior for this is debatable, and should really be offered
+	 * as a property. By returning false, all keyboard actions are
+	 * implemented in terms of the table. By returning true, the
+	 * tree would get a chance to do something with the keyboard
+	 * events. For the most part this is ok. But for certain keys,
+	 * such as left/right, the tree will expand/collapse where as
+	 * the table focus should really move to a different column. Page
+	 * up/down should also be implemented in terms of the table.
+	 * By returning false this also has the added benefit that clicking
+	 * outside of the bounds of the tree node, but still in the tree
+	 * column will select the row, whereas if this returned true
+	 * that wouldn't be the case.
+	 * <p>By returning false we are also enforcing the policy that
+	 * the tree will never be editable (at least by a key sequence).
+	 */
+	public boolean isCellEditable(EventObject e) {
+	    if (e instanceof MouseEvent) {
+		for (int counter = getColumnCount() - 1; counter >= 0;
+		     counter--) {
+		    if (getColumnClass(counter) == TreeTableModel.class) {
+			MouseEvent me = (MouseEvent)e;
+			MouseEvent newME = new MouseEvent(tree, me.getID(),
+				   me.getWhen(), me.getModifiers(),
+				   me.getX() - getCellRect(0, counter, true).x,
+				   me.getY(), me.getClickCount(),
+                                   me.isPopupTrigger());
+			tree.dispatchEvent(newME);
+			break;
+		    }
+		}
+	    }
+	    return false;
+	}
+    }
+
+
+    /**
+     * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
+     * to listen for changes in the ListSelectionModel it maintains. Once
+     * a change in the ListSelectionModel happens, the paths are updated
+     * in the DefaultTreeSelectionModel.
+     */
+    class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { 
+	/** Set to true when we are updating the ListSelectionModel. */
+	protected boolean         updatingListSelectionModel;
+
+	public ListToTreeSelectionModelWrapper() {
+	    super();
+	    getListSelectionModel().addListSelectionListener
+	                            (createListSelectionListener());
+	}
+
+	/**
+	 * Returns the list selection model. ListToTreeSelectionModelWrapper
+	 * listens for changes to this model and updates the selected paths
+	 * accordingly.
+	 */
+	ListSelectionModel getListSelectionModel() {
+	    return listSelectionModel; 
+	}
+
+	/**
+	 * This is overridden to set <code>updatingListSelectionModel</code>
+	 * and message super. This is the only place DefaultTreeSelectionModel
+	 * alters the ListSelectionModel.
+	 */
+	public void resetRowSelection() {
+	    if(!updatingListSelectionModel) {
+		updatingListSelectionModel = true;
+		try {
+		    super.resetRowSelection();
+		}
+		finally {
+		    updatingListSelectionModel = false;
+		}
+	    }
+	    // Notice how we don't message super if
+	    // updatingListSelectionModel is true. If
+	    // updatingListSelectionModel is true, it implies the
+	    // ListSelectionModel has already been updated and the
+	    // paths are the only thing that needs to be updated.
+	}
+
+	/**
+	 * Creates and returns an instance of ListSelectionHandler.
+	 */
+	protected ListSelectionListener createListSelectionListener() {
+	    return new ListSelectionHandler();
+	}
+
+	/**
+	 * If <code>updatingListSelectionModel</code> is false, this will
+	 * reset the selected paths from the selected rows in the list
+	 * selection model.
+	 */
+	protected void updateSelectedPathsFromSelectedRows() {
+	    if(!updatingListSelectionModel) {
+		updatingListSelectionModel = true;
+		try {
+		    // This is way expensive, ListSelectionModel needs an
+		    // enumerator for iterating.
+		    int        min = listSelectionModel.getMinSelectionIndex();
+		    int        max = listSelectionModel.getMaxSelectionIndex();
+
+		    clearSelection();
+		    if(min != -1 && max != -1) {
+			for(int counter = min; counter <= max; counter++) {
+			    if(listSelectionModel.isSelectedIndex(counter)) {
+				TreePath     selPath = tree.getPathForRow
+				                            (counter);
+
+				if(selPath != null) {
+				    addSelectionPath(selPath);
+				}
+			    }
+			}
+		    }
+		}
+		finally {
+		    updatingListSelectionModel = false;
+		}
+	    }
+	}
+
+	/**
+	 * Class responsible for calling updateSelectedPathsFromSelectedRows
+	 * when the selection of the list changse.
+	 */
+	class ListSelectionHandler implements ListSelectionListener {
+	    public void valueChanged(ListSelectionEvent e) {
+		updateSelectedPathsFromSelectedRows();
+	    }
+	}
+    }
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,84 @@
+/*
+ * MergeSort.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+/**
+ * An implementation of MergeSort, needs to be subclassed to
+ * compare the terms.
+ *
+ * @author Scott Violet
+ */
+public abstract class MergeSort extends Object {
+    protected Object           toSort[];
+    protected Object           swapSpace[];
+
+    public void sort(Object array[]) {
+	if(array != null && array.length > 1)
+	{
+	    int             maxLength;
+  
+	    maxLength = array.length;
+	    swapSpace = new Object[maxLength];
+	    toSort = array;
+	    this.mergeSort(0, maxLength - 1);
+	    swapSpace = null;
+	    toSort = null;
+	}
+    }
+
+    public abstract int compareElementsAt(int beginLoc, int endLoc);
+
+    protected void mergeSort(int begin, int end) {
+	if(begin != end)
+	{
+	    int           mid;
+
+	    mid = (begin + end) / 2;
+	    this.mergeSort(begin, mid);
+	    this.mergeSort(mid + 1, end);
+	    this.merge(begin, mid, end);
+	}
+    }
+
+    protected void merge(int begin, int middle, int end) {
+	int           firstHalf, secondHalf, count;
+
+	firstHalf = count = begin;
+	secondHalf = middle + 1;
+	while((firstHalf <= middle) && (secondHalf <= end))
+	{
+	    if(this.compareElementsAt(secondHalf, firstHalf) < 0)
+		swapSpace[count++] = toSort[secondHalf++];
+	    else
+		swapSpace[count++] = toSort[firstHalf++];
+	}
+	if(firstHalf <= middle)
+	{
+	    while(firstHalf <= middle)
+		swapSpace[count++] = toSort[firstHalf++];
+	}
+	else
+	{
+	    while(secondHalf <= end)
+		swapSpace[count++] = toSort[secondHalf++];
+	}
+	for(count = begin;count <= end;count++)
+	    toSort[count] = swapSpace[count];
+    }
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,480 @@
+/*
+ * TreeTableExample2.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import javax.swing.tree.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.text.NumberFormat;
+
+/**
+ * Assembles the UI. The UI consists of a JTreeTable and a status label.
+ * As nodes are loaded by the FileSystemModel2, in a background thread,
+ * the status label updates as well as the renderer to draw the node that
+ * is being loaded differently.
+ *
+ * @author Scott Violet
+ * @author Philip Milne
+ */
+public class TreeTableExample2 {
+    /** Number of instances of TreeTableExample2. */
+    protected static int         ttCount;
+
+    /** Model for the JTreeTable. */
+    protected FileSystemModel2    model;
+    /** Used to represent the model. */
+    protected JTreeTable         treeTable;
+    /** Row the is being reloaded. */
+    protected int                reloadRow;
+    /** TreePath being reloaded. */
+    protected TreePath           reloadPath;
+    /** A counter increment as the Timer fies and the same path is
+     * being reloaded. */
+    protected int                reloadCounter;
+    /** Timer used to update reload state. */
+    protected Timer              timer;
+    /** Used to indicate status. */
+    protected JLabel             statusLabel;
+    /** Frame containing everything. */
+    protected JFrame             frame;
+    /** Path created with. */
+    protected String             path;
+
+
+    public TreeTableExample2(String path) {
+	this.path = path;
+	ttCount++;
+
+	frame = createFrame();
+
+	Container      cPane = frame.getContentPane();
+	JMenuBar       mb = createMenuBar();
+
+	model = createModel(path);
+	treeTable = createTreeTable();
+	statusLabel = createStatusLabel();
+	cPane.add(new JScrollPane(treeTable));
+	cPane.add(statusLabel, BorderLayout.SOUTH);
+
+	reloadRow = -1;
+	frame.setJMenuBar(mb);
+	frame.pack();
+	frame.show();
+	SwingUtilities.invokeLater(new Runnable() {
+	    public void run() {
+		reload(model.getRoot());
+	    }
+	});
+    }
+
+    /**
+     * Creates and return a JLabel that is used to indicate the status
+     * of loading.
+     */
+    protected JLabel createStatusLabel() {
+	JLabel         retLabel = new JLabel(" ");
+
+	retLabel.setHorizontalAlignment(JLabel.RIGHT);
+	retLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));
+	return retLabel;
+    }
+
+    /**
+     * Creates and returns the instanceof JTreeTable that will be used.
+     * This also creates, but does not start, the Timer that is used to
+     * update the display as files are loaded.
+     */
+    protected JTreeTable createTreeTable() {
+	JTreeTable       treeTable = new JTreeTable(model);
+
+	treeTable.getColumnModel().getColumn(1).setCellRenderer
+	                           (new IndicatorRenderer());
+
+	Reloader rl = new Reloader();
+
+	timer = new Timer(700, rl);
+	timer.setRepeats(true);
+	treeTable.getTree().addTreeExpansionListener(rl);
+	return treeTable;
+    }
+
+    /**
+     * Creates the FileSystemModel2 that will be used.
+     */
+    protected FileSystemModel2 createModel(String path) {
+	return new FileSystemModel2(path);
+    }
+
+    /**
+     * Creates the JFrame that will contain everything.
+     */
+    protected JFrame createFrame() {
+	JFrame       retFrame = new JFrame("TreeTable II");
+
+	retFrame.addWindowListener(new WindowAdapter() {
+	    public void windowClosing(WindowEvent we) {
+		if (--ttCount == 0) {
+		    System.exit(0);
+		}
+	    }
+	});
+	return retFrame;
+    }
+
+    /**
+     * Creates a menu bar.
+     */
+    protected JMenuBar createMenuBar() {
+        JMenu            fileMenu = new JMenu("File"); 
+	JMenuItem        menuItem;
+
+	menuItem = new JMenuItem("Open");
+	menuItem.addActionListener(new ActionListener() {
+	    public void actionPerformed(ActionEvent ae) {
+		JFileChooser      fc = new JFileChooser(path);
+
+		fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+
+		int               result = fc.showOpenDialog(frame);
+
+		if (result == JFileChooser.APPROVE_OPTION) {
+		    String      newPath = fc.getSelectedFile().getPath();
+
+		    new TreeTableExample2(newPath);
+		}
+	    }
+	});
+	fileMenu.add(menuItem);
+	fileMenu.addSeparator();
+	
+	menuItem = new JMenuItem("Reload");
+	menuItem.addActionListener(new ActionListener() {
+	    public void actionPerformed(ActionEvent ae) {
+		TreePath         path = treeTable.getTree().getSelectionPath();
+
+		if (path != null) {
+		    model.stopLoading();
+		    reload(path.getLastPathComponent());
+		}
+	    }
+	});
+	fileMenu.add(menuItem);
+
+	menuItem = new JMenuItem("Stop");
+	menuItem.addActionListener(new ActionListener() {
+	    public void actionPerformed(ActionEvent ae) {
+		model.stopLoading();
+	    }
+	});
+	fileMenu.add(menuItem);
+
+	fileMenu.addSeparator();
+
+	menuItem = new JMenuItem("Exit"); 
+	menuItem.addActionListener(new ActionListener() {
+	    public void actionPerformed(ActionEvent ae) {
+		System.exit(0);
+	    }
+	});
+	fileMenu.add(menuItem); 
+
+
+	// Create a menu bar
+	JMenuBar        menuBar = new JMenuBar();
+
+	menuBar.add(fileMenu);
+
+	// Menu for the look and feels (lafs).
+	UIManager.LookAndFeelInfo[]        lafs = UIManager.
+	                                    getInstalledLookAndFeels();
+	ButtonGroup                        lafGroup = new ButtonGroup();
+
+	JMenu          optionsMenu = new JMenu("Options");
+
+	menuBar.add(optionsMenu);
+
+	for(int i = 0; i < lafs.length; i++) {
+	    JRadioButtonMenuItem rb = new JRadioButtonMenuItem(lafs[i].
+							       getName()); 
+	    optionsMenu.add(rb);
+	    rb.setSelected(UIManager.getLookAndFeel().getName().equals
+			   (lafs[i].getName()));
+	    rb.putClientProperty("UIKey", lafs[i]);
+	    rb.addItemListener(new ItemListener() {
+	        public void itemStateChanged(ItemEvent ae) {
+	            JRadioButtonMenuItem rb2 = (JRadioButtonMenuItem)ae.
+			                       getSource();
+	            if(rb2.isSelected()) {
+		        UIManager.LookAndFeelInfo       info =
+                                      (UIManager.LookAndFeelInfo)
+			               rb2.getClientProperty("UIKey");
+		        try {
+		            UIManager.setLookAndFeel(info.getClassName());
+		            SwingUtilities.updateComponentTreeUI(frame);
+			}
+			catch (Exception e) {
+		             System.err.println("unable to set UI " +
+						e.getMessage());
+			}
+	            }
+	        }
+	    });
+	    lafGroup.add(rb);
+	}
+	return menuBar;
+    }
+
+    /**
+     * Invoked to reload the children of a particular node. This will
+     * also restart the timer.
+     */
+    protected void reload(Object node) {
+	model.reloadChildren(node);
+	if (!timer.isRunning()) {
+	    timer.start();
+	}
+    }
+
+    /**
+     * Updates the status label based on reloadRow.
+     */
+    protected void updateStatusLabel() {
+	if (reloadPath != null) {
+	    statusLabel.setText("Reloading: " + model.getPath
+				(reloadPath.getLastPathComponent()));
+	    if ((reloadCounter % 4) < 2) {
+		statusLabel.setForeground(Color.red);
+	    }
+	    else {
+		statusLabel.setForeground(Color.blue);
+	    }
+	}
+	else if (!model.isReloading()) {
+	    statusLabel.setText("Total Size: " + NumberFormat.getInstance().
+				format(model.getTotalSize(model.getRoot())));
+	    statusLabel.setForeground(Color.black);
+	}
+    }
+
+
+    /**
+     * Reloader is the ActionListener used in the Timer. In response to
+     * the timer updating it will reset the reloadRow/reloadPath and
+     * generate the necessary event so that the display will update. It
+     * also implements the TreeExpansionListener so that if the tree is
+     * altered while loading the reloadRow is updated accordingly.
+     */
+    class Reloader implements ActionListener, TreeExpansionListener {
+	public void actionPerformed(ActionEvent ae) {
+	    if (!model.isReloading()) {
+		// No longer loading.
+		timer.stop();
+		if (reloadRow != -1) {
+		    generateChangeEvent(reloadRow);
+		}
+		reloadRow = -1;
+		reloadPath = null;
+	    }
+	    else {
+		// Still loading, see if paths changed.
+		TreePath       newPath = model.getPathLoading();
+
+		if (newPath == null) {
+		    // Hmm... Will usually indicate the reload thread
+		    // completed between time we asked if reloading.
+		    if (reloadRow != -1) {
+			generateChangeEvent(reloadRow);
+		    }
+		    reloadRow = -1;
+		    reloadPath = null;
+		}
+		else {
+		    // Ok, valid path, see if matches last path.
+		    int        newRow = treeTable.getTree().getRowForPath
+			                          (newPath);
+
+		    if (newPath.equals(reloadPath)) {
+			reloadCounter = (reloadCounter + 1) % 8;
+			if (newRow != reloadRow) {
+			    int             lastRow = reloadRow;
+
+			    reloadRow = newRow;
+			    generateChangeEvent(lastRow);
+			}
+			generateChangeEvent(reloadRow);
+		    }
+		    else {
+			int          lastRow = reloadRow;
+
+			reloadCounter = 0;
+			reloadRow = newRow;
+			reloadPath = newPath;
+			if (lastRow != reloadRow) {
+			    generateChangeEvent(lastRow);
+			}
+			generateChangeEvent(reloadRow);
+		    }
+		}
+	    }
+	    updateStatusLabel();
+	}
+
+	/**
+	 * Generates and update event for the specified row. FileSystemModel2
+	 * could do this, but it would not know when the row has changed
+	 * as a result of expanding/collapsing nodes in the tree.
+	 */
+	protected void generateChangeEvent(int row) {
+	    if (row != -1) {
+		AbstractTableModel     tModel = (AbstractTableModel)treeTable.
+		                                 getModel();
+
+		tModel.fireTableChanged(new TableModelEvent
+				       (tModel, row, row, 1));
+	    }
+	}
+
+	//
+	// TreeExpansionListener
+	//
+
+	/**
+	 * Invoked when the tree has expanded.
+	 */
+	public void treeExpanded(TreeExpansionEvent te) {
+	    updateRow();
+	}
+
+	/**
+	 * Invoked when the tree has collapsed.
+	 */
+	public void treeCollapsed(TreeExpansionEvent te) {
+	    updateRow();
+	}
+
+	/**
+	 * Updates the reloadRow and path, this does not genernate a
+	 * change event.
+	 */
+	protected void updateRow() {
+	    reloadPath = model.getPathLoading();
+
+	    if (reloadPath != null) {
+		reloadRow = treeTable.getTree().getRowForPath(reloadPath);
+	    }
+	}
+    }
+
+
+    /**
+     * A renderer that will give an indicator when a cell is being reloaded.
+     */
+    class IndicatorRenderer extends DefaultTableCellRenderer {
+	/** Makes sure the number of displayed in an internationalized
+	 * manner. */
+	protected NumberFormat       formatter;
+	/** Row that is currently being painted. */
+	protected int                lastRow;
+	
+
+	IndicatorRenderer() {
+	    setHorizontalAlignment(JLabel.RIGHT);
+	    formatter = NumberFormat.getInstance();
+	}
+
+	/**
+	 * Invoked as part of DefaultTableCellRenderers implemention. Sets
+	 * the text of the label.
+	 */
+	public void setValue(Object value) { 
+	    setText((value == null) ? "---" : formatter.format(value)); 
+	}
+
+	/**
+	 * Returns this.
+	 */
+	public Component getTableCellRendererComponent(JTable table,
+			    Object value, boolean isSelected, boolean hasFocus,
+			    int row, int column) {
+	    super.getTableCellRendererComponent(table, value, isSelected,
+						hasFocus, row, column);
+	    lastRow = row;
+	    return this;
+	}
+
+	/**
+	 * If the row being painted is also being reloaded this will draw
+	 * a little indicator.
+	 */
+	public void paint(Graphics g) {
+	    if (lastRow == reloadRow) {
+		int       width = getWidth();
+		int       height = getHeight();
+
+		g.setColor(getBackground());
+		g.fillRect(0, 0, width, height);
+		g.setColor(getForeground());
+
+		int       diameter = Math.min(width, height);
+
+		if (reloadCounter < 5) {
+		    g.fillArc((width - diameter) / 2, (height - diameter) / 2,
+			      diameter, diameter, 90, -(reloadCounter * 90));
+		}
+		else {
+		    g.fillArc((width - diameter) / 2, (height - diameter) / 2,
+			      diameter, diameter, 90,
+			      (4 - reloadCounter % 4) * 90);
+		}
+	    }
+	    else {
+		super.paint(g);
+	    }
+	}
+    }
+
+
+    public static void main(String[] args) {
+	if (args.length > 0) {
+	    for (int counter = args.length - 1; counter >= 0; counter--) {
+		new TreeTableExample2(args[counter]);
+	    }
+	}
+	else {
+	    String            path;
+
+	    try {
+		path = System.getProperty("user.home");
+		if (path != null) {
+		    new TreeTableExample2(path);
+		}
+	    }
+	    catch (SecurityException se) {
+		path = null;
+	    }
+	    if (path == null) {
+		System.out.println("Could not determine home directory");
+	    }
+	}
+    }
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,68 @@
+/*
+ * TreeTableModel.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information").  You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import javax.swing.tree.TreeModel;
+
+/**
+ * TreeTableModel is the model used by a JTreeTable. It extends TreeModel
+ * to add methods for getting inforamtion about the set of columns each 
+ * node in the TreeTableModel may have. Each column, like a column in 
+ * a TableModel, has a name and a type associated with it. Each node in 
+ * the TreeTableModel can return a value for each of the columns and 
+ * set that value if isCellEditable() returns true. 
+ *
+ * @author Philip Milne 
+ * @author Scott Violet
+ */
+public interface TreeTableModel extends TreeModel
+{
+    /**
+     * Returns the number ofs availible column.
+     */
+    public int getColumnCount();
+
+    /**
+     * Returns the name for column number <code>column</code>.
+     */
+    public String getColumnName(int column);
+
+    /**
+     * Returns the type for column number <code>column</code>.
+     */
+    public Class getColumnClass(int column);
+
+    /**
+     * Returns the value to be displayed for node <code>node</code>, 
+     * at column number <code>column</code>.
+     */
+    public Object getValueAt(Object node, int column);
+
+    /**
+     * Indicates whether the the value for node <code>node</code>, 
+     * at column number <code>column</code> is editable.
+     */
+    public boolean isCellEditable(Object node, int column);
+
+    /**
+     * Sets the value for node <code>node</code>, 
+     * at column number <code>column</code>.
+     */
+    public void setValueAt(Object aValue, Object node, int column);
+}

Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java	2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java	2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,125 @@
+/*
+ * @(#)TreeTableModelAdapter.java	1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.tree.TreePath;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+
+/**
+ * This is a wrapper class takes a TreeTableModel and implements 
+ * the table model interface. The implementation is trivial, with 
+ * all of the event dispatching support provided by the superclass: 
+ * the AbstractTableModel. 
+ *
+ * @version 1.2 10/27/98
+ *
+ * @author Philip Milne
+ * @author Scott Violet
+ */
+public class TreeTableModelAdapter extends AbstractTableModel
+{
+    JTree tree;
+    TreeTableModel treeTableModel;
+
+    public TreeTableModelAdapter(TreeTableModel treeTableModel, JTree tree) {
+        this.tree = tree;
+        this.treeTableModel = treeTableModel;
+
+	tree.addTreeExpansionListener(new TreeExpansionListener() {
+	    // Don't use fireTableRowsInserted() here; the selection model
+	    // would get updated twice. 
+	    public void treeExpanded(TreeExpansionEvent event) {  
+	      fireTableDataChanged(); 
+	    }
+            public void treeCollapsed(TreeExpansionEvent event) {  
+	      fireTableDataChanged(); 
+	    }
+	});
+
+	// Install a TreeModelListener that can update the table when
+	// tree changes. We use delayedFireTableDataChanged as we can
+	// not be guaranteed the tree will have finished processing
+	// the event before us.
+	treeTableModel.addTreeModelListener(new TreeModelListener() {
+	    public void treeNodesChanged(TreeModelEvent e) {
+		delayedFireTableDataChanged();
+	    }
+
+	    public void treeNodesInserted(TreeModelEvent e) {
+		delayedFireTableDataChanged();
+	    }
+
+	    public void treeNodesRemoved(TreeModelEvent e) {
+		delayedFireTableDataChanged();
+	    }
+
+	    public void treeStructureChanged(TreeModelEvent e) {
+		delayedFireTableDataChanged();
+	    }
+	});
+    }
+
+    // Wrappers, implementing TableModel interface. 
+
+    public int getColumnCount() {
+	return treeTableModel.getColumnCount();
+    }
+
+    public String getColumnName(int column) {
+	return treeTableModel.getColumnName(column);
+    }
+
+    public Class getColumnClass(int column) {
+	return treeTableModel.getColumnClass(column);
+    }
+
+    public int getRowCount() {
+	return tree.getRowCount();
+    }
+
+    protected Object nodeForRow(int row) {
+	TreePath treePath = tree.getPathForRow(row);
+	return treePath.getLastPathComponent();         
+    }
+
+    public Object getValueAt(int row, int column) {
+	return treeTableModel.getValueAt(nodeForRow(row), column);
+    }
+
+    public boolean isCellEditable(int row, int column) {
+         return treeTableModel.isCellEditable(nodeForRow(row), column); 
+    }
+
+    public void setValueAt(Object value, int row, int column) {
+	treeTableModel.setValueAt(value, nodeForRow(row), column);
+    }
+
+    /**
+     * Invokes fireTableDataChanged after all the pending events have been
+     * processed. SwingUtilities.invokeLater is used to handle this.
+     */
+    protected void delayedFireTableDataChanged() {
+	SwingUtilities.invokeLater(new Runnable() {
+	    public void run() {
+		fireTableDataChanged();
+	    }
+	});
+    }
+}
+




More information about the pkg-java-commits mailing list