United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4765272 REGRESSION: IAE: focusCycleRoot not focus cyle root of a Component
JDK-4765272 : REGRESSION: IAE: focusCycleRoot not focus cyle root of a Component

Details
Type:
Bug
Submit Date:
2002-10-18
Status:
Resolved
Updated Date:
2002-11-16
Project Name:
JDK
Resolved Date:
2002-11-16
Component:
client-libs
OS:
windows_nt
Sub-Component:
javax.swing
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.4.0
Fixed Versions:
1.4.2 (mantis)

Related Reports

Sub Tasks

Description

Name: sv35042			Date: 10/18/2002


FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_01-b03)
Java HotSpot(TM) Client VM (build 1.4.0_01-b03, mixed mode)

FULL OPERATING SYSTEM VERSION :
4NT  3.01A   Windows NT 4.00

A DESCRIPTION OF THE PROBLEM :
First of all, this problem does not appear in jdk 1.3.1.
In version 1.4 (and also in 1.4.0_01), when a component is
removed from a container, the container tries to set the
focus on to the next component, if the component being
removed had the focus. If the next component in the focus
traversal list is disabled, then the following exception is
thrown.

java.lang.IllegalArgumentException: focusCycleRoot is not a
focus cyle root of a Component
        at
javax.swing.SortingFocusTraversalPolicy.getComponentAfter
(SortingFocusTraversalPolicy.java:195)
        at
javax.swing.LayoutFocusTraversalPolicy.getComponentAfter
(LayoutFocusTraversalPolicy.java:85)
        at
javax.swing.LegacyGlueFocusTraversalPolicy.getComponentAfter
(LegacyGlueFocusTraversalPolicy.java:63)
        at java.awt.Component.nextFocusHelper
(Component.java:6156)
        at java.awt.Container.nextFocusHelper
(Container.java:2197)
        at java.awt.Component.removeNotify
(Component.java:5436)
        at java.awt.Container.removeNotify
(Container.java:1883)
        at javax.swing.JComponent.removeNotify
(JComponent.java:4286)
        at java.awt.Container.removeAll(Container.java:609)

.
.
.


REGRESSION.  Last worked in version 1.3.1

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.Create a panel and add 3 JTextFields to it. Let's call
them text1, text2 and text3
2.text1.setNextFocusableComponent(text3);
3.text3.setEnabled(false); //so that control cannot really
go to text3
4.add the panel to a frame and show it
5.Now try to remove all the components from frame (say when
someone tries to close the frame. There is a reason why we
are having to do this instead of disposing the frame
directly). The following method would do this. (Pass
reference to the frame as a parameter to this method):
 
    public static void removeComponentsFromContainer
(Container c) {
         if (c == null) return;
         Component[] components = c.getComponents();
         for (int i = 0; i < components.length; i++) {
             if (components[i] instanceof Container) {
                 removeComponentsFromContainer((Container)
components[i]);
             }
         }
         c.removeAll();
     }

This throws the exception mentioned above. This piece of
code does not throw any exception under jdk 1.3.
(full source code included)

EXPECTED VERSUS ACTUAL BEHAVIOR :
Excepted result: The code should be able to remove all the
components
Actual result: Exception being thrown

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.IllegalArgumentException: focusCycleRoot is not a focus cyle root of
a Component
        at javax.swing.SortingFocusTraversalPolicy.getComponentAfter
(SortingFocusTraversalPolicy.java:195)
        at javax.swing.LayoutFocusTraversalPolicy.getComponentAfter
(LayoutFocusTraversalPolicy.java:85)
        at javax.swing.LegacyGlueFocusTraversalPolicy.getComponentAfter
(LegacyGlueFocusTraversalPolicy.java:63)
        at java.awt.Component.nextFocusHelper(Component.java:6156)
        at java.awt.Container.nextFocusHelper(Container.java:2197)
        at java.awt.Component.removeNotify(Component.java:5436)
        at java.awt.Container.removeNotify(Container.java:1883)
        at javax.swing.JComponent.removeNotify(JComponent.java:4286)
        at java.awt.Container.removeAll(Container.java:609)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class CloseTest
{
    public static JPanel getPanel()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(4,2));

        JLabel label1 = new JLabel("label 1");
        JLabel label2 = new JLabel("label 2");
        JLabel label3 = new JLabel("label 3");
        JTextField text1 = new JTextField("label 1 text");
        JTextField text2 = new JTextField("label 2 text");
        JTextField text3 = new JTextField("label 3 text");
        panel.add(label1);
        panel.add(text1);
        panel.add(label2);
        panel.add(text2);
        panel.add(label3);
        panel.add(text3);
        label2.requestFocus();
        panel.setBorder(BorderFactory.createEtchedBorder());
        text1.setNextFocusableComponent(text3);
        text3.setEnabled(false);
        return panel;
    }
    public static void removeComponentsFromContainer(Container c)
     {
         if (c == null) return;
         Component[] components = c.getComponents();
         for (int i = 0; i < components.length; i++)
         {
             if (components[i] instanceof Container)
             {
                 removeComponentsFromContainer((Container) components[i]);
             }
         }
         c.removeAll();
     }
    
    public static void main(String[] args)
    {
        try
        {
        UIManager.setLookAndFeel(
	                "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        final JFrame frame = new JFrame("Close Test");
        Container container = frame.getContentPane();
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                removeComponentsFromContainer(frame);
                System.exit(0);
            }
        });
        container.add(getPanel());
        frame.setLocation(400,300);
        frame.setSize(300,150);
        frame.show();
    }
}
---------- END SOURCE ----------

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: 153584) 
======================================================================

                                    

Comments
SUGGESTED FIX



Name: apR10133			Date: 11/05/2002



------- LegacyGlueFocusTraversalPolicy.java -------
*** /tmp/sccs.uRa4uu	Mon Nov  4 21:59:19 2002
--- LegacyGlueFocusTraversalPolicy.java	Mon Nov  4 21:56:27 2002
***************
*** 59,65 ****
              prevHardCoded = hardCoded;
              hardCoded = (Component)forwardMap.get(hardCoded);
              if (hardCoded == null) {
!                 if (delegatePolicy != null) {
                      return delegatePolicy.getComponentAfter(focusCycleRoot,
                                                              prevHardCoded);
                  } else if (delegateManager != null) {
--- 59,66 ----
              prevHardCoded = hardCoded;
              hardCoded = (Component)forwardMap.get(hardCoded);
              if (hardCoded == null) {
!                 if (delegatePolicy != null &&
! 		    prevHardCoded.isFocusCycleRoot(focusCycleRoot)) {
                      return delegatePolicy.getComponentAfter(focusCycleRoot,
                                                              prevHardCoded);
                  } else if (delegateManager != null) {
***************
*** 87,93 ****
              prevHardCoded = hardCoded;
              hardCoded = (Component)backwardMap.get(hardCoded);
              if (hardCoded == null) {
!                 if (delegatePolicy != null) {
                      return delegatePolicy.getComponentBefore(focusCycleRoot,
                                                         prevHardCoded);
                  } else if (delegateManager != null) {
--- 88,95 ----
              prevHardCoded = hardCoded;
              hardCoded = (Component)backwardMap.get(hardCoded);
              if (hardCoded == null) {
!                 if (delegatePolicy != null &&
! 		    prevHardCoded.isFocusCycleRoot(focusCycleRoot)) {
                      return delegatePolicy.getComponentBefore(focusCycleRoot,
                                                         prevHardCoded);
                  } else if (delegateManager != null) {

###@###.###

======================================================================
                                     
2004-08-24
WORK AROUND

Two options:

. Clear out focus before removing the container:                KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
 Because focus is removed asynchronously you'll have to remove your components
  using an invoke later, eg:
            public void windowClosing(WindowEvent e) {
                KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        removeComponentsFromContainer(frame);
                        System.exit(0);
                    }
                });
            }

. Option two is to use remove(int) instead of removeAll, eg:
    public static void removeComponentsFromContainer(Container c)
     {
         if (c == null) return;
         Component[] components = c.getComponents();
         for (int i = 0; i < components.length; i++)
         {
             if (components[i] instanceof Container)
             {
                 removeComponentsFromContainer((Container) components[i]);
             }
             c.remove(0);
         }
     }
                                     
2004-08-24
EVALUATION

This needs to be fixed for mantis.
###@###.### 2002-10-18

Name: apR10133			Date: 11/05/2002


The container's removeAll removes its children in the back order. When
the panel removes first (focused) button LegacyGlueFocusTraversalPolicy
attempts to transfer focus to the hardcoded forward component (i.e. to
the third button). The third button is not displayable (as it is alredy
removed from components hierarchy), hence calls getComponentAfter() for
the third button. But frame is no more the FCR for third button, hence
exception is thrown.

when we set next focusable component the invoker component (current
component)register this next focusable component and add it to the
forwardMap of LegacyGlueFocusTraversalPolicy. Unregistration of next
focusable component occur when the current component is removed from
component hierarchy. So if we remove the "next focusable component"
from component hierarchy there is no any unregistration happen and
the forwardMap point to the invalid component.

As the "next focusable component" could be a Component, but not
JComponent, it would be resonable to check if the hardcoded component
is valid before we call getComponentAfter().

###@###.###

======================================================================
                                     
2004-08-24
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
mantis

FIXED IN:
mantis

INTEGRATED IN:
mantis
mantis-b08


                                     
2004-08-24



Hardware and Software, Engineered to Work Together