United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4760426 JTree DnD conflicts with JTree.setUI(..)
JDK-4760426 : JTree DnD conflicts with JTree.setUI(..)

Details
Type:
Bug
Submit Date:
2002-10-09
Status:
Closed
Updated Date:
2005-05-20
Project Name:
JDK
Resolved Date:
2005-05-20
Component:
client-libs
OS:
windows_nt,windows_2000
Sub-Component:
javax.swing
CPU:
x86
Priority:
P4
Resolution:
Duplicate
Affected Versions:
1.4.0,1.4.2
Fixed Versions:
6

Related Reports
Backport:
Duplicate:
Duplicate:
Duplicate:
Relates:
Relates:
Relates:

Sub Tasks

Description
Name: sv35042			Date: 10/09/2002


FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-
b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version
5.00.2195]

ADDITIONAL OPERATING SYSTEMS :
Also on Linux

A DESCRIPTION OF THE PROBLEM :
JTrees with enabled drop support produce NullPointerExceptions when
setUI() was called on them.
It doesn't matter which TreeUI is used
(Windows, Metal, Basic, as soon as the TreeUI is changed the failure
appears.
This bug appears on Windows and Linux.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.Compile and execute the program
2.Select with a click a component of
the left JTree
3.Drag this component to the textarea
4.Do steps 2+3
with the right JTree
5.You will see a NullPointerException on the
console

EXPECTED VERSUS ACTUAL BEHAVIOR :
The left JTree is working properly, whereas the right JTree produces
NullPointerExceptions when drags are moved out of the JTree.
Code is
working so far but there are NullPointerExceptions.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.NullPointerException
        at
javax.swing.plaf.basic.BasicTreeUI$TreeDropTargetListener.restoreComponentState(BasicTreeUI.java:3680)
 
       at
javax.swing.plaf.basic.BasicDropTargetListener.dragExit(BasicDropTargetListener.java:249)
 
       at javax.swing.TransferHandler$SwingDropTarget.dragExit(TransferHandler.java:579)
        
at
sun.awt.dnd.SunDropTargetContextPeer.processExitMessage(SunDropTargetContextPeer.java:396)
 
       at
sun.awt.dnd.SunDropTargetContextPeer.access$700(SunDropTargetContextPeer.java:52)
 
       at
sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchExitEvent(SunDropTargetContextPeer.java:793)
 
       at
sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:741)
 
       at sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:29)
        at
java.awt.Component.dispatchEventImpl(Component.java:3384)
        at
java.awt.Container.dispatchEventImpl(Container.java:1437)
        at
java.awt.Component.dispatchEvent(Component.java:3367)
        at
java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3214)
        at
java.awt.LightweightDispatcher.trackMouseEnterExit(Container.java:3046)
        at
java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:2992)
        at
java.awt.LightweightDispatcher.dispatchEvent(Container.java:2854)
        at
java.awt.Container.dispatchEventImpl(Container.java:1423)
        at
java.awt.Window.dispatchEventImpl(Window.java:1566)
        at
java.awt.Component.dispatchEvent(Component.java:3367)
        at
java.awt.EventQueue.dispatchEvent(EventQueue.java:445)
        at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:190)
 
       at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:144)
 
       at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
        at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:130)
        at
java.awt.EventDispatchThread.run(EventDispatchThread.java:98)

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import
java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;
import
javax.swing.tree.*;
import javax.swing.plaf.metal.MetalTreeUI;


public class
PanTree extends JFrame  {
  private JTree jtree;
  private JTree ofjtree;
  private
JScrollPane jscp;
  private JScrollPane ofjscp;
public static void main(String args[]){
  
PanTree pt = new PanTree();
}
public PanTree(){
  super();
  try{
    
UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
  }catch
(Exception e){
    System.err.println(e);
  }
  getContentPane().setLayout(new
FlowLayout());
  setSize(800,500);
  jtree = new JTree();
  
jtree.setDragEnabled(true);
  jtree.setTransferHandler(new TreeTransferHandler());
  
jtree.setScrollsOnExpand(true);
  
jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
 
 jscp =  new JScrollPane(jtree);
  
  
  ofjtree = new JTree();
  
ofjtree.setDragEnabled(true);
  ofjtree.setTransferHandler(new
TreeTransferHandler());
  ofjtree.setScrollsOnExpand(true);
  
ofjtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
 
 ofjtree.setUI(new MetalTreeUI()); // this causes NullPointerExceptions
  ofjscp =  new
JScrollPane(ofjtree);
  JTextArea textarea = new JTextArea(30,30);
  
textarea.setDragEnabled(true);

  getContentPane().add(jscp);
  
getContentPane().add(textarea);
  getContentPane().add(ofjscp);
  
addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
      
System.out.println("trying to exit");
      System.exit(0);
    }
  });
  pack();
  
setVisible(true);
}

  class TreeTransferHandler extends TransferHandler{
    private
DataFlavor[] plainFlavors = null;
    
    TreeTransferHandler(){
      super();
      try {
        
plainFlavors = new DataFlavor[1];
        plainFlavors[0] = new
DataFlavor("text/plain;class=java.lang.String");
      }catch (ClassNotFoundException
cle){
        System.err.println("error initializing TreeTransferable");
      }
    }
    protected
Transferable createTransferable(JComponent c) {
      if (c instanceof JTree) {
        JTree tree =
(JTree) c;
        TreePath path = tree.getSelectionPath();
        return new TreeTransferable(tree,
path);
      }
      return null;
    }

    public int getSourceActions(JComponent c) {
      return
COPY;
    }
    
    public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
      
return true;
    }
 
    class TreeTransferable implements Transferable{
      private TreePath
path;
      private JTree tree;

      TreeTransferable(JTree tree, TreePath path) {
        this.path =
path;
        this.tree = tree;
      }

      public DataFlavor[] getTransferDataFlavors() {
        return
plainFlavors;
      }

      public boolean isDataFlavorSupported(DataFlavor flavor) {
        for (int
i = 0; i < plainFlavors.length; i++) {
          if (plainFlavors[i].equals(flavor)) {
            return
true;
          }
        }
        return false;
      }

      public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException{
        String data = getPlainData();
        data = (data == null) ?
"" : data;
        if (String.class.equals(flavor.getRepresentationClass())) {
          return data;
        
}
        throw new UnsupportedFlavorException(flavor);
      }

      protected String getPlainData()
{
        TreeModel model = tree.getModel();
        Object node = path.getLastPathComponent();
        
return path.toString()+"\n";
      }
    }
  }
}
---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Do not call setUI() on JTrees when using dop!

Release Regression From : 1.4
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Review ID: 144125) 
======================================================================

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
dragon
mustang


                                     
2004-09-25
EVALUATION

I can reproduce this. Not a regression though, part of new DnD behavior.
###@###.### 2002-10-18

Note that I have closed 4846941 as a duplicate of this bug. That bug simply states that the problem affects multiple components. All affected components will be fixed when this bug is addressed.

The problem is that in our UI classes, we fail to remove our DropTargetListeners when the UI is uninstalled. We therefore can end up with multiple DropTargetListeners. These multiple listeners conflict with each other.
###@###.### 2003-04-11

The fixes for 4468566 and 4942851 will re-architect Swing's drop support to make it much more usable by the developer. In particular, they will provide location sensitive dropping, and much better indication of the drop location. As a result of this re-architecture, the UI classes will no longer install DropTargetListeners, as all the details of indicating drop locations will be handled within TransferHandler. This eliminates the source of these exceptions. Closing as a duplicate.
###@###.### 2005-05-20 15:27:24 GMT
                                     
2005-05-20
WORK AROUND

While it's ideal to pull this work-around once you target Mustang, this work-around isn't expected to cause any problems if you don't.

The nature of this bug is that every time the UI is switched on your JTree, a drop listener is left behind by the old UI, causing conflicts with the new listener. The proper fix is for Swing to keep track of this and remove it. The work-around is simply a couple of lines that forces this to occur.

Note that this is a potential problem for any JTree, JTable, JList, or JTextComponent with a TransferHandler. As such, you may want to apply the change to all such components.

Here's one way to do it, by subclassing and overridding the setUI methods of all affected components:

tree = new JTree() {
    public void setUI(TreeUI newUI) {
        super.setUI(newUI);
        TransferHandler handler = getTransferHandler();
        setTransferHandler(null);
        setTransferHandler(handler);
    }
};

Alternatively, you can simply call the following on any affected component after a UI switch:

TransferHandler handler = component.getTransferHandler();
component.setTransferHandler(null);
component.setTransferHandler(handler);
                                     
2005-08-23
EVALUATION

Since this bug isn't fixed until Mustang, I've added a work-around for earlier releases in the work around section.
                                     
2005-08-23



Hardware and Software, Engineered to Work Together