-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
1.4.2
-
Cause Known
-
x86
-
windows_2000
Name: gm110360 Date: 11/07/2003
FULL PRODUCT VERSION :
java -version
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
I like the idea of the new long-term persistence, but I've
found the implementation to have some bugs.For example:
1. JInternalFrame does not persist properly (the bounds and
visibility are not set).
2. BorderLayout doesn't persist properly without a center
component (last component is always placed in the center).
3. JPopupMenu.add(Action) doesn't persist properly
(exception raised) (workaround: popup.add(new JMenuItem
(Action)) does work.)
4. Inner classes can't persist (no public constructor).
(Yet inner classes are Sun's argument against c# delegates.
The new EventHandler is a poor approximation to delegates
with no compile-time checks.)
For the BorderLayout, the problem is that this:
c.add(comp, BorderLayout.EAST)
is not quite the same as:
c.add(comp);
c.getLayout().addLayoutComponent(BorderLayout.EAST, be);
The latter is what the XMLEncoder creates for the former.
The latter is equivalent to:
c.add(comp, BorderLayout.CENTER);
c.getLayout().addLayoutComponent(BorderLayout.EAST, be);
The problem is that the same component is both the CENTER
and the EAST component in the BorderLayout! BorderLayout
assigns bounds to the EAST first and then to the CENTER;
our poor component gets its bounds set twice and lands in
the center, with space taken in the east for a phantom
version.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Use the XMLEncoder to write a JFrame with a JDesktopPane
and a JInternalFrame. Use XMLDecoder to reload it. The
JInternal will not be visible because its bounds are not
set and setVisible(true) is not called.
2. Use the XMLEncoder to write a component with
BorderLayout and one child in the NORTH (any of N,S,E,W
will do). No child should be in the center. Use the
XMLDecoder to reload it. The component will be placed in
the center (and a phantom will be in the North).
EXPECTED VERSUS ACTUAL BEHAVIOR :
The XMLEncoder is not writing the bounds and visibility of
a JInternalFrame. The frame should appear in the same
place it was before saving.
The XMLEncoder does not write save BorderLayouts properly.
The component should live in the proper location.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
c:\Documents and Settings\kbeyer\My Documents\src\dax\test>java BadEncoder
java BadEncoder
writing
java.lang.InstantiationException: javax.swing.JPopupMenu$2
Continuing ...
java.lang.Exception: discarding statement JPopupMenu0.add(JPopupMenu$20);
Continuing ...
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
BadEncoder.java:
----------------
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import dax.awt.*;
// MY OWN PersistenceDelegate for a JInternalFrame
class javax_swing_JInternalFrame_PersistenceDelegate extends
DefaultPersistenceDelegate {
protected void initialize(Class type, Object oldInstance, Object
newInstance, Encoder out)
{
super.initialize(type, oldInstance, newInstance, out);
JInternalFrame oldC = (JInternalFrame)oldInstance;
JInternalFrame newC = (JInternalFrame)newInstance;
// bounds
Rectangle oldB = oldC.getBounds();
Rectangle newB = newC.getBounds();
if( ! oldB.equals(newB) ) {
out.writeStatement(new Statement(oldInstance, "setBounds", new
Object[]{oldB}));
}
// visible
boolean oldV = oldC.isVisible();
boolean newV = newC.isVisible();
if (newV != oldV) {
out.writeStatement(new Statement(oldInstance, "setVisible", new
Object[]{Boolean.valueOf(oldV)}));
}
}
}
public class BadEncoder
{
private static boolean fixPopup = false;
private static boolean fixJInternalFrame = false;
private static boolean fixBorderLayout = false;
public static void decode() throws IOException
{
XMLDecoder in = new XMLDecoder(
new BufferedInputStream(new FileInputStream("test.xml")));
in.readObject();
in.close();
}
public static void encode() throws Exception
{
JFrame topFrame = new JFrame();
topFrame.setBounds(0,0,500,500);
JDesktopPane desktop = new JDesktopPane();
topFrame.getContentPane().add(desktop);
JInternalFrame frame = new JInternalFrame("my
frame",true,true,true,true);
frame.setVisible(true);
frame.setBounds(10,10,100,100);
JComponent c = (JComponent) frame.getContentPane();
c.add(new JButton("button"), BorderLayout.NORTH);
if( fixBorderLayout ) {
c.add(new JPanel(), BorderLayout.CENTER);
}
desktop.add(frame);
JPopupMenu popup = new JPopupMenu();
//this does not persist properly (exception due to JPopupMenu inner
class):
if( !fixPopup ) {
popup.add(new ExitAction());
} else {
popup.add(new JMenuItem(new ExitAction()));
}
// try{ popup.add(new SimpleAction("Exit",
SimpleAction.class, "doExit")); } catch(Exception ex) {}
//Add listener to components that can bring up popup menus.
MouseListener popupListener = new PopupListener(popup);
desktop.addMouseListener(popupListener);
topFrame.setVisible(true);
Thread.sleep(2000);
System.err.println("\nwriting\n");
XMLEncoder e = new XMLEncoder(
new BufferedOutputStream(new FileOutputStream("test.xml")));
if( fixJInternalFrame ) {
e.setPersistenceDelegate(JInternalFrame.class,
new
javax_swing_JInternalFrame_PersistenceDelegate());
}
e.writeObject(topFrame);
e.close();
}
public static void main(String args[]) throws Exception
{
boolean doDecode = false;
for(int i = 0 ; i < args.length ; i++) {
if( "-decode".equals(args[i]) ) {
doDecode = true;
}
else if( "-fixPopup".equals(args[i]) ) {
fixPopup = true;
}
else if( "-fixJInternalFrame".equals(args[i]) ) {
fixJInternalFrame = true;
}
else if( "-fixBorderLayout".equals(args[i]) ) {
fixBorderLayout = true;
}
else {
System.err.println("unknown argument: "+args[i]);
}
}
if( doDecode ) {
decode();
} else {
encode();
System.exit(0);
}
}
}
PopupListener.java
------------------
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
// NOTE THE EXTRA FILES FOR THE MOUSEADAPTER AND THE ACTION!
// THESE ARE NEEDED FOR PERSISTENCE BEFORE THEY WERE INNER CLASSES.
// (EventHandler might be a usable substitute.)
public class PopupListener extends MouseAdapter
{
JPopupMenu popup;
public PopupListener()
{
}
public PopupListener(JPopupMenu popup)
{
this.popup = popup;
}
public JPopupMenu getPopup() { return popup; }
public void setPopup(JPopupMenu popup) { this.popup = popup; }
public void mousePressed(MouseEvent e)
{
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e)
{
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e)
{
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),
e.getX(), e.getY());
}
}
}
ExitAction.java
---------------
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
public class ExitAction extends AbstractAction
{
public ExitAction()
{
super("Exit");
}
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
To run:
javac BadEncoder.java PopupListener.java ExitAction.java
java BadEncoder
java BadEncoder -decode
java BadEncoder -fixPopup
java BadEncoder -decode
java BadEncoder -fixPopup -fixJInternalFrame
java BadEncoder -decode
java BadEncoder -fixPopup -fixJInternalFrame
java BadEncoder -decode
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
workarounds shown in source code
(Incident Review ID: 179580)
======================================================================
FULL PRODUCT VERSION :
java -version
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
I like the idea of the new long-term persistence, but I've
found the implementation to have some bugs.For example:
1. JInternalFrame does not persist properly (the bounds and
visibility are not set).
2. BorderLayout doesn't persist properly without a center
component (last component is always placed in the center).
3. JPopupMenu.add(Action) doesn't persist properly
(exception raised) (workaround: popup.add(new JMenuItem
(Action)) does work.)
4. Inner classes can't persist (no public constructor).
(Yet inner classes are Sun's argument against c# delegates.
The new EventHandler is a poor approximation to delegates
with no compile-time checks.)
For the BorderLayout, the problem is that this:
c.add(comp, BorderLayout.EAST)
is not quite the same as:
c.add(comp);
c.getLayout().addLayoutComponent(BorderLayout.EAST, be);
The latter is what the XMLEncoder creates for the former.
The latter is equivalent to:
c.add(comp, BorderLayout.CENTER);
c.getLayout().addLayoutComponent(BorderLayout.EAST, be);
The problem is that the same component is both the CENTER
and the EAST component in the BorderLayout! BorderLayout
assigns bounds to the EAST first and then to the CENTER;
our poor component gets its bounds set twice and lands in
the center, with space taken in the east for a phantom
version.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Use the XMLEncoder to write a JFrame with a JDesktopPane
and a JInternalFrame. Use XMLDecoder to reload it. The
JInternal will not be visible because its bounds are not
set and setVisible(true) is not called.
2. Use the XMLEncoder to write a component with
BorderLayout and one child in the NORTH (any of N,S,E,W
will do). No child should be in the center. Use the
XMLDecoder to reload it. The component will be placed in
the center (and a phantom will be in the North).
EXPECTED VERSUS ACTUAL BEHAVIOR :
The XMLEncoder is not writing the bounds and visibility of
a JInternalFrame. The frame should appear in the same
place it was before saving.
The XMLEncoder does not write save BorderLayouts properly.
The component should live in the proper location.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
c:\Documents and Settings\kbeyer\My Documents\src\dax\test>java BadEncoder
java BadEncoder
writing
java.lang.InstantiationException: javax.swing.JPopupMenu$2
Continuing ...
java.lang.Exception: discarding statement JPopupMenu0.add(JPopupMenu$20);
Continuing ...
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
BadEncoder.java:
----------------
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import dax.awt.*;
// MY OWN PersistenceDelegate for a JInternalFrame
class javax_swing_JInternalFrame_PersistenceDelegate extends
DefaultPersistenceDelegate {
protected void initialize(Class type, Object oldInstance, Object
newInstance, Encoder out)
{
super.initialize(type, oldInstance, newInstance, out);
JInternalFrame oldC = (JInternalFrame)oldInstance;
JInternalFrame newC = (JInternalFrame)newInstance;
// bounds
Rectangle oldB = oldC.getBounds();
Rectangle newB = newC.getBounds();
if( ! oldB.equals(newB) ) {
out.writeStatement(new Statement(oldInstance, "setBounds", new
Object[]{oldB}));
}
// visible
boolean oldV = oldC.isVisible();
boolean newV = newC.isVisible();
if (newV != oldV) {
out.writeStatement(new Statement(oldInstance, "setVisible", new
Object[]{Boolean.valueOf(oldV)}));
}
}
}
public class BadEncoder
{
private static boolean fixPopup = false;
private static boolean fixJInternalFrame = false;
private static boolean fixBorderLayout = false;
public static void decode() throws IOException
{
XMLDecoder in = new XMLDecoder(
new BufferedInputStream(new FileInputStream("test.xml")));
in.readObject();
in.close();
}
public static void encode() throws Exception
{
JFrame topFrame = new JFrame();
topFrame.setBounds(0,0,500,500);
JDesktopPane desktop = new JDesktopPane();
topFrame.getContentPane().add(desktop);
JInternalFrame frame = new JInternalFrame("my
frame",true,true,true,true);
frame.setVisible(true);
frame.setBounds(10,10,100,100);
JComponent c = (JComponent) frame.getContentPane();
c.add(new JButton("button"), BorderLayout.NORTH);
if( fixBorderLayout ) {
c.add(new JPanel(), BorderLayout.CENTER);
}
desktop.add(frame);
JPopupMenu popup = new JPopupMenu();
//this does not persist properly (exception due to JPopupMenu inner
class):
if( !fixPopup ) {
popup.add(new ExitAction());
} else {
popup.add(new JMenuItem(new ExitAction()));
}
// try{ popup.add(new SimpleAction("Exit",
SimpleAction.class, "doExit")); } catch(Exception ex) {}
//Add listener to components that can bring up popup menus.
MouseListener popupListener = new PopupListener(popup);
desktop.addMouseListener(popupListener);
topFrame.setVisible(true);
Thread.sleep(2000);
System.err.println("\nwriting\n");
XMLEncoder e = new XMLEncoder(
new BufferedOutputStream(new FileOutputStream("test.xml")));
if( fixJInternalFrame ) {
e.setPersistenceDelegate(JInternalFrame.class,
new
javax_swing_JInternalFrame_PersistenceDelegate());
}
e.writeObject(topFrame);
e.close();
}
public static void main(String args[]) throws Exception
{
boolean doDecode = false;
for(int i = 0 ; i < args.length ; i++) {
if( "-decode".equals(args[i]) ) {
doDecode = true;
}
else if( "-fixPopup".equals(args[i]) ) {
fixPopup = true;
}
else if( "-fixJInternalFrame".equals(args[i]) ) {
fixJInternalFrame = true;
}
else if( "-fixBorderLayout".equals(args[i]) ) {
fixBorderLayout = true;
}
else {
System.err.println("unknown argument: "+args[i]);
}
}
if( doDecode ) {
decode();
} else {
encode();
System.exit(0);
}
}
}
PopupListener.java
------------------
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
// NOTE THE EXTRA FILES FOR THE MOUSEADAPTER AND THE ACTION!
// THESE ARE NEEDED FOR PERSISTENCE BEFORE THEY WERE INNER CLASSES.
// (EventHandler might be a usable substitute.)
public class PopupListener extends MouseAdapter
{
JPopupMenu popup;
public PopupListener()
{
}
public PopupListener(JPopupMenu popup)
{
this.popup = popup;
}
public JPopupMenu getPopup() { return popup; }
public void setPopup(JPopupMenu popup) { this.popup = popup; }
public void mousePressed(MouseEvent e)
{
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e)
{
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e)
{
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),
e.getX(), e.getY());
}
}
}
ExitAction.java
---------------
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
public class ExitAction extends AbstractAction
{
public ExitAction()
{
super("Exit");
}
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
To run:
javac BadEncoder.java PopupListener.java ExitAction.java
java BadEncoder
java BadEncoder -decode
java BadEncoder -fixPopup
java BadEncoder -decode
java BadEncoder -fixPopup -fixJInternalFrame
java BadEncoder -decode
java BadEncoder -fixPopup -fixJInternalFrame
java BadEncoder -decode
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
workarounds shown in source code
(Incident Review ID: 179580)
======================================================================
- relates to
-
JDK-6349028 Undefined behavior of BorderLayout when component changes constraints
- Open
-
JDK-6353636 Different behavior of JMenu and JPopupMenu
- Open
-
JDK-6437265 LTP: some Component is missing during xml serializing
- Resolved
-
JDK-4994649 Long term persistence fails for JLayeredPane
- Closed