JDK-8317857 : JScrollBar.getMinimumSize() breaks the contract of JComponent.setMinimumSize()
  • Type: CSR
  • Component: client-libs
  • Sub-Component: javax.swing
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 23
  • Submitted: 2023-10-11
  • Updated: 2024-02-01
  • Resolved: 2024-02-01
Related Reports
CSR :  
Description
Summary
-------

`JComponent`'s `setMinimumSize` and `setMaximumSize` are overridden in `JScrollBar` to specify that `JScrollBar`'s `getMinimumSize` and `getMaximumSize` returns a size derived from the preferred size in one axis and fixed value in the other axis.


Problem
-------

The javadoc for `JComponent.setMinimumSize(java.awt.Dimension)` states:

"Sets the minimum size of this component to a constant value.
Subsequent calls to `getMinimumSize` will always return this value..."

However, `JScrollBar` overrides `getMinimumSize()` and breaks this contract.

The returned `Dimension` is composed of the preferred width and a fixed minimum height, or vice versa
depending on the orientation of the scrollbar.

Similarly, `JScrollBar.getMaximumSize()` uses the preferred width or height and a fixed maximum for the other dimension.

There's risk that changing this might cause the layout of applications to change in a way that breaks the applications. That change should have been made a long time ago.


Solution
--------

`JScrollBar`'s `setMinimumSize()` and `setMaximumSize()` are overridden to clarify the spec


Specification
-------------

javax.swing.JComponent.setMaximumSize

         /**
          * Sets the maximum size of this component to a constant
    -     * value.  Subsequent calls to <code>getMaximumSize</code> will always
    +     * value.  Subsequent calls to {@code getMaximumSize} will always
          * return this value; the component's UI will not be asked
    -     * to compute it.  Setting the maximum size to <code>null</code>
    +     * to compute it. Setting the maximum size to {@code null}
          * restores the default behavior.
    +     * <p>
    +     * Subclasses may choose to override this by returning their own maximum size
    +     * in the {@code getMaximumSize} method.
          *
          * @param maximumSize a <code>Dimension</code> containing the
          *          desired maximum allowable size
          * @see #getMaximumSize
          */
         public void setMaximumSize(Dimension maximumSize) {


javax.swing.JComponent.setMinimumSize

         /**
          * Sets the minimum size of this component to a constant
    -     * value.  Subsequent calls to <code>getMinimumSize</code> will always
    +     * value.  Subsequent calls to {@code getMinimumSize} will always
          * return this value; the component's UI will not be asked
    -     * to compute it.  Setting the minimum size to <code>null</code>
    +     * to compute it. Setting the minimum size to {@code null}
          * restores the default behavior.
    +     * <p>
    +     * Subclasses may choose to override this by returning their own minimum size
    +     * in the {@code getMinimumSize} method.
          *
          * @param minimumSize the new minimum size of this component
          * @see #getMinimumSize
          */
         public void setMinimumSize(Dimension minimumSize) {


javax.swing.JScrollBar.setMinimumSize

    +    /**
    +     * Unlike most components, {@code JScrollBar} derives the minimum size from
    +     * the preferred size in one axis and a fixed minimum size in the other.
    +     * Thus, it overrides {@code JComponent.setMinimumSize} contract
    +     * that subsequent calls to {@code getMinimumSize} will return the
    +     * same value as set in {@code JComponent.setMinimumSize}.
    +     *
    +     * @param minimumSize the new minimum size of this component
    +     */
    +    public void setMinimumSize(Dimension minimumSize) {


javax.swing.JScrollBar.setMaximumSize

         /**
    +     * Unlike most components, {@code JScrollBar} derives the maximum size from
    +     * the preferred size in one axis and a fixed maximum size in the other.
    +     * Thus, it overrides {@code JComponent.setMaximumSize} contract
    +     * that subsequent calls to {@code getMaximumSize} will return the
    +     * same value as set in {@code JComponent.setMaximumSize}.
    +     *
    +     * @param maximumSize the desired maximum allowable size
    +     */
    +    public void setMaximumSize(Dimension maximumSize) {


javax.swing.JScrollBar.getMinimumSize

         /**
    -     * The scrollbar is flexible along it's scrolling axis and
    +     * Returns the minimum size for the {@code JScrollBar}.
    +     * The scrollbar is flexible along its scrolling axis and
          * rigid along the other axis.
    +     * As specified in {@code setMinimumSize} JScrollBar will derive the
    +     * minimum size from the preferred size in one axis and a
    +     * fixed minimum size in the other.
    +     *
    +     * @return the minimum size as specified above
          */
         public Dimension getMinimumSize() {


javax.swing.JScrollBar.getMaximumSize

         /**
    -     * The scrollbar is flexible along it's scrolling axis and
    +     * Returns the maximum size for the {@code JScrollBar}.
    +     * The scrollbar is flexible along its scrolling axis and
          * rigid along the other axis.
    +     * As specified in {@code setMaximumSize} JScrollBar will derive the
    +     * maximum size from the preferred size in one axis and a
    +     * fixed maximum size in the other.
    +     *
    +     * @return the maximum size as specified above
          */
         public Dimension getMaximumSize() {

Comments
Moving to Approved.
01-02-2024

[~darcy] JComponent superclass specifications is updated to address that subclasses may override the said contract.. Please have a look..
29-01-2024

[~aivanov], I am advocating that the superclass specifications be updated to allow the behavior that is already taking place. Abstractly, the superclass class says "This method does X." That method does do X in most subclasses, but a few subclasses do Y instead. So if the superclass specs were updated to say "This method does X or Y", that would address my spec concerns here.
25-01-2024

[~darcy] Do you mean that the specification for `JComponent.getMinimumSize` and `JComponent.getMaximumSize` should be modified to allow returning *any* value, not necessarily the one that was set by corresponding `set-` method and not necessarily call UI delegate? I tend to agree with Phil. Components aren't interchangeable. A scrollbar is not the same as a list or tree. There's at least one more public component, `BasicArrowButton`, which overrides min/max size and don't follow the contract established by `JComponent`. However, this class isn't used as an independent component.
25-01-2024

I think the first sentence should use the present tense: “JScrollBar <del>will derive</del> <ins><i>derives</i></ins> the minimum size from the preferred size…”. It's a simple statement, it does not depend on any condition. Since the specification for `JScrollBar.getMinimumSize` and `JScrollBar.getMaximumSize` is also modified, should it be amended so that the first sentence, which is displayed in the method summary, provided a summary of what is returned? Currently, it describes a property of `JScrollBar`.
25-01-2024

Moving to Provisional to indicate solving this issue is reasonable; details should be agreed upon before the request is Finalized.
23-01-2024

For JDK-8283758, the behavior was very atypical and required extensive discussion since the change violated the usual subtyping properties. So we are using the same terminology, the Liskov substitution principle (LSP) (https://en.wikipedia.org/wiki/Liskov_substitution_principle) generally means that a subtype should follow the stated contracst of its supertypes. Strictly speaking the changes here do not follow that principle.
22-01-2024

" There were issues of that character discussed as part of JDK-8283758" Looks to me like this doesn't modify the superclass (ie InputStream) doc, it just says that the subclass behaves contrary to it, which matches what we are already proposing here. " Random.setSeed spec:" Reading the text of the CSR, the related issue is about Random::next but I can't correlate the stated problem that "it appears to impose a general contract on subclasses" with the apiNote that appears to address it. My preference is to leave alone the doc of JComponent.
19-01-2024

[~prr] a few examples: * There were issues of that character discussed as part of JDK-8283758 * Random.setSeed spec: https://bugs.openjdk.org/browse/JDK-8282928?focusedId=14483086&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14483086
19-01-2024

[~darcy] do you have a specific example of this ? I'm inclined to leave the superclass alone as I don't expect any other case like this but if required we'd probably just add something vague like "If necessary, a subclass may implement a different policy, which must be properly specified and documented".
19-01-2024

[~psadhukhan], should the contract of the superclass also be changed to allow the behavior of this subclass? We've made analogous amendments to superclass contracts in similar situations.
19-01-2024

[~prr] Can you please review and amend, as appropriate, the wordings for these methods removal?
03-11-2023

[~prr] Thanks for taking a look. Anything more needs to be added? I see that you have not added as a Reviewer..
19-10-2023

[~prr] Canyou please have a look at this CSR?
17-10-2023