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