JDK-6682046 : Mixing code does not always recalculate shapes correctly when resizing components
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 6u7,7
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,linux,windows_xp,windows_vista generic,linux,windows_xp,windows_vista
  • CPU: generic,x86
  • Submitted: 2008-03-31
  • Updated: 2011-03-11
  • Resolved: 2008-09-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b36Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
The following test fails to enlarge the button:

import java.awt.*;

public class Test {

    public static void main(String[] args) {
        Frame f = new Frame();
        Button b = new Button("ok");
        f.add(b);
        f.setExtendedState(Frame.MAXIMIZED_BOTH);
        f.pack();
        f.setVisible(true);
    }
}

This is a regression of the fix for CR 6637796.

Comments
SUGGESTED FIX --- old/src/share/classes/java/awt/Button.java 2008-05-22 16:38:35.000000000 +0400 +++ new/src/share/classes/java/awt/Button.java 2008-05-22 16:38:34.000000000 +0400 @@ -213,7 +213,7 @@ } // This could change the preferred size of the Component. - if (testvalid && valid) { + if (testvalid && isValid()) { invalidate(); } } --- old/src/share/classes/java/awt/Checkbox.java 2008-05-22 16:38:36.000000000 +0400 +++ new/src/share/classes/java/awt/Checkbox.java 2008-05-22 16:38:35.000000000 +0400 @@ -284,7 +284,7 @@ } // This could change the preferred size of the Component. - if (testvalid && valid) { + if (testvalid && isValid()) { invalidate(); } } --- old/src/share/classes/java/awt/Choice.java 2008-05-22 16:38:37.000000000 +0400 +++ new/src/share/classes/java/awt/Choice.java 2008-05-22 16:38:36.000000000 +0400 @@ -207,7 +207,7 @@ } // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } @@ -269,7 +269,7 @@ } // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } @@ -299,7 +299,7 @@ } // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } @@ -323,7 +323,7 @@ } // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } @@ -367,7 +367,7 @@ } // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } --- old/src/share/classes/java/awt/Component.java 2008-05-22 16:38:37.000000000 +0400 +++ new/src/share/classes/java/awt/Component.java 2008-05-22 16:38:37.000000000 +0400 @@ -345,7 +345,7 @@ * @see #validate * @see #invalidate */ - volatile boolean valid = false; + private volatile boolean valid = false; /** * The <code>DropTarget</code> associated with this component. @@ -1061,6 +1061,9 @@ * @since JDK1.0 */ public boolean isValid() { + return isValid_NoClientCode(); + } + final boolean isValid_NoClientCode() { return (peer != null) && valid; } @@ -1699,7 +1702,7 @@ // This could change the preferred size of the Component. // Fix for 6213660. Should compare old and new fonts and do not // call invalidate() if they are equal. - if (valid && f != oldFont && (oldFont == null || + if (isValid() && f != oldFont && (oldFont == null || !oldFont.equals(f))) { invalidate(); } @@ -1758,7 +1761,7 @@ firePropertyChange("locale", oldValue, l); // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } @@ -2069,7 +2072,7 @@ if (resized) { invalidate(); } - if (parent != null && parent.valid) { + if (parent != null && parent.isValid_NoClientCode()) { parent.invalidate(); } } @@ -2639,7 +2642,8 @@ public void validate() { synchronized (getTreeLock()) { ComponentPeer peer = this.peer; - if (!valid && peer != null) { + boolean wasValid = isValid_NoClientCode(); + if (!isValid && peer != null) { Font newfont = getFont(); Font oldfont = peerFont; if (newfont != oldfont && (oldfont == null @@ -2650,6 +2654,9 @@ peer.layout(); } valid = true; + if (!wasValid) { + mixOnValidating(); + } } } @@ -2678,7 +2685,7 @@ if (!isMaximumSizeSet()) { maxSize = null; } - if (parent != null && parent.valid) { + if (parent != null && parent.isValid_NoClientCode()) { parent.invalidate(); } } @@ -7691,7 +7698,7 @@ protected String paramString() { String thisName = getName(); String str = (thisName != null? thisName : "") + "," + x + "," + y + "," + width + "x" + height; - if (!valid) { + if (!isValid()) { str += ",invalid"; } if (!visible) { @@ -8451,7 +8458,7 @@ firePropertyChange("componentOrientation", oldValue, o); // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } @@ -9295,7 +9302,8 @@ */ private boolean areBoundsValid() { Container cont = getContainer(); - return cont == null || cont.isValid() || cont.getLayout() == null; + return cont == null || cont.isValid_NoClientCode() + || cont.getLayout() == null; } /** @@ -9566,5 +9574,8 @@ } } + void mixOnValidating() { + } + // ****************** END OF MIXING CODE ******************************** } --- old/src/share/classes/java/awt/Container.java 2008-05-22 16:38:39.000000000 +0400 +++ new/src/share/classes/java/awt/Container.java 2008-05-22 16:38:38.000000000 +0400 @@ -500,7 +500,7 @@ ncomponents - index - 1); component[--ncomponents] = null; - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } } else { @@ -804,7 +804,7 @@ } } - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } if (peer != null) { @@ -1090,7 +1090,7 @@ comp.numListening(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK)); adjustDescendants(comp.countHierarchyMembers()); - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } if (peer != null) { @@ -1186,7 +1186,7 @@ ncomponents - index - 1); component[--ncomponents] = null; - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } if (containerListener != null || @@ -1286,7 +1286,7 @@ if (peer != null && layoutMgr == null && isVisible()) { updateCursorImmediately(); } - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } } @@ -1448,7 +1448,7 @@ */ public void setLayout(LayoutManager mgr) { layoutMgr = mgr; - if (valid) { + if (isValid()) { invalidate(); } } @@ -1522,10 +1522,10 @@ */ public void validate() { /* Avoid grabbing lock unless really necessary. */ - if (!valid) { + if (!isValid_NoClientCode()) { boolean updateCur = false; synchronized (getTreeLock()) { - if (!valid && peer != null) { + if (!isValid_NoClientCode() && peer != null) { ContainerPeer p = null; if (peer instanceof ContainerPeer) { p = (ContainerPeer) peer; @@ -1534,7 +1534,6 @@ p.beginValidate(); } validateTree(); - valid = true; if (p != null) { p.endValidate(); updateCur = isVisible(); @@ -1557,7 +1556,7 @@ * @see #validate */ protected void validateTree() { - if (!valid) { + if (!isValid_NoClientCode()) { if (peer instanceof ContainerPeer) { ((ContainerPeer)peer).beginLayout(); } @@ -1567,7 +1566,7 @@ Component comp = component[i]; if ( (comp instanceof Container) && !(comp instanceof Window) - && !comp.valid) { + && !comp.isValid_NoClientCode()) { ((Container)comp).validateTree(); } else { comp.validate(); @@ -1577,7 +1576,7 @@ ((ContainerPeer)peer).endLayout(); } } - valid = true; + super.validate(); } /** @@ -1592,12 +1591,12 @@ ((Container)comp).invalidateTree(); } else { - if (comp.valid) { + if (comp.isValid_NoClientCode()) { comp.invalidate(); } } } - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } } @@ -4111,6 +4110,21 @@ } } + @Override + void mixOnValidating() { + synchronized (getTreeLock()) { + if (mixingLog.isLoggable(Level.FINE)) { + mixingLog.fine("this = " + this); + } + + if (hasHeavyweightDescendants()) { + recursiveApplyCurrentShape(); + } + + super.mixOnValidating(); + } + } + // ****************** END OF MIXING CODE ******************************** } --- old/src/share/classes/java/awt/Dialog.java 2008-05-22 16:38:40.000000000 +0400 +++ new/src/share/classes/java/awt/Dialog.java 2008-05-22 16:38:39.000000000 +0400 @@ -1327,7 +1327,7 @@ // the insets of the Dialog. If we could, we'd call invalidate() // from the peer, but we need to guarantee that we're not holding // the Dialog lock when we call invalidate(). - if (testvalid && valid) { + if (testvalid && isValid()) { invalidate(); } } --- old/src/share/classes/java/awt/Frame.java 2008-05-22 16:38:41.000000000 +0400 +++ new/src/share/classes/java/awt/Frame.java 2008-05-22 16:38:40.000000000 +0400 @@ -590,7 +590,7 @@ if (peer != null) { mbManagement = true; menuBar.addNotify(); - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } peer.setMenuBar(menuBar); @@ -633,7 +633,7 @@ // the insets of the Frame. If we could, we'd call invalidate() // from the peer, but we need to guarantee that we're not holding // the Frame lock when we call invalidate(). - if (testvalid && valid) { + if (testvalid && isValid()) { invalidate(); } firePropertyChange("resizable", oldResizable, resizable); @@ -907,7 +907,7 @@ FramePeer peer = (FramePeer)this.peer; if (peer != null) { mbManagement = true; - if (valid) { + if (isValid_NoClientCode()) { invalidate(); } peer.setMenuBar(null); --- old/src/share/classes/java/awt/Label.java 2008-05-22 16:38:42.000000000 +0400 +++ new/src/share/classes/java/awt/Label.java 2008-05-22 16:38:41.000000000 +0400 @@ -257,7 +257,7 @@ } // This could change the preferred size of the Component. - if (testvalid && valid) { + if (testvalid && isValid()) { invalidate(); } } --- old/src/share/classes/java/awt/TextField.java 2008-05-22 16:38:43.000000000 +0400 +++ new/src/share/classes/java/awt/TextField.java 2008-05-22 16:38:42.000000000 +0400 @@ -296,7 +296,7 @@ super.setText(t); // This could change the preferred size of the Component. - if (valid) { + if (isValid()) { invalidate(); } } --- /dev/null 2008-04-09 14:36:21.096432000 +0400 +++ new/test/java/awt/Mixing/Validating.java 2008-05-22 16:38:43.000000000 +0400 @@ -0,0 +1,382 @@ +/* + @test + @bug 6682046 + @summary Mixing code does not always recalculate shapes correctly when resizing components + @author anthony.petrov@...: area=awt.mixing + @library ../regtesthelpers + @build Util + @run main Validating +*/ + +/** + * Validating.java + * + * summary: Mixing code does not always recalculate shapes correctly when resizing components + */ + +import java.awt.*; +import java.awt.event.*; +import test.java.awt.regtesthelpers.Util; + +public class Validating +{ + static volatile boolean clickPassed = false; + + private static void init() + { + //*** Create instructions for the user here *** + + String[] instructions = + { + "This is an AUTOMATIC test, simply wait until it is done.", + "The result (passed or failed) will be shown in the", + "message window below." + }; + Sysout.createDialog( ); + Sysout.printInstructions( instructions ); + + if (!Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH)) { + System.out.println("The test environment does not support maximization. The test cannot be performed."); + pass(); + return; + } + + // Create the frame with a button. + Frame f = new Frame(); + Button b = new Button("ok"); + b.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + clickPassed = true; + } + }); + f.add(b); + // Make the frame maximized + f.setExtendedState(Frame.MAXIMIZED_BOTH); + f.pack(); + f.setVisible(true); + + Robot robot = Util.createRobot(); + robot.setAutoDelay(20); + + Util.waitForIdle(robot); + + // Now let's attempt to click in the middle of the button + // (i.e. in the middle of the window). + // If the button doesn't receive the click, it means that the test + // failed: the shape of the button was not enlarged. + Point heavyLoc = b.getLocationOnScreen(); + robot.mouseMove(heavyLoc.x + b.getWidth() / 2, heavyLoc.y + b.getHeight() / 2); + + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + Util.waitForIdle(robot); + + if (clickPassed) { + pass(); + } else { + fail("The button cannot be clicked."); + } + }//End init() + + + + /***************************************************** + * Standard Test Machinery Section + * DO NOT modify anything in this section -- it's a + * standard chunk of code which has all of the + * synchronisation necessary for the test harness. + * By keeping it the same in all tests, it is easier + * to read and understand someone else's test, as + * well as insuring that all tests behave correctly + * with the test harness. + * There is a section following this for test- + * classes + ******************************************************/ + private static boolean theTestPassed = false; + private static boolean testGeneratedInterrupt = false; + private static String failureMessage = ""; + + private static Thread mainThread = null; + + private static int sleepTime = 300000; + + // Not sure about what happens if multiple of this test are + // instantiated in the same VM. Being static (and using + // static vars), it aint gonna work. Not worrying about + // it for now. + public static void main( String args[] ) throws InterruptedException + { + mainThread = Thread.currentThread(); + try + { + init(); + } + catch( TestPassedException e ) + { + //The test passed, so
13-05-2008

EVALUATION It seems that fixing this issue by introducing an additional workaround doesn't make any sense. The root cause of this issue (as well as the 6637796) is the fact that the 'valid' property doesn't throw any notifications upon changes. However, the mixing code needs to handle changes to this property and recalculate the shapes of the effected components. Which means that in order to fix this issue we need to rearchitect the validate()/invalidate()/isValid() machinery so that it allows to hook into the process of validating/invalidating a component. The main task would be to identify all the locations of direct accessing of the 'valid' property and change them to use the setter/getter pair. After that, making the valid property private, we may be sure that we can safely hook into the setter and catch all the modifications to this property, and therefore we'll be able to correctly recalculate the shapes of components in appropriate moments of time.
01-04-2008