1 /*
   2  * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 import java.awt.Component;
  28 import java.awt.Container;
  29 import java.awt.Dimension;
  30 import java.awt.Insets;
  31 import java.awt.LayoutManager2;
  32 import java.util.*;
  33 import static java.awt.Component.BaselineResizeBehavior;
  34 import static javax.swing.LayoutStyle.ComponentPlacement;
  35 import static javax.swing.SwingConstants.HORIZONTAL;
  36 import static javax.swing.SwingConstants.VERTICAL;
  37 
  38 /**
  39  * {@code GroupLayout} is a {@code LayoutManager} that hierarchically
  40  * groups components in order to position them in a {@code Container}.
  41  * {@code GroupLayout} is intended for use by builders, but may be
  42  * hand-coded as well.
  43  * Grouping is done by instances of the {@link Group Group} class. {@code
  44  * GroupLayout} supports two types of groups. A sequential group
  45  * positions its child elements sequentially, one after another. A
  46  * parallel group aligns its child elements in one of four ways.
  47  * <p>
  48  * Each group may contain any number of elements, where an element is
  49  * a {@code Group}, {@code Component}, or gap. A gap can be thought
  50  * of as an invisible component with a minimum, preferred and maximum
  51  * size. In addition {@code GroupLayout} supports a preferred gap,
  52  * whose value comes from {@code LayoutStyle}.
  53  * <p>
  54  * Elements are similar to a spring. Each element has a range as
  55  * specified by a minimum, preferred and maximum.  Gaps have either a
  56  * developer-specified range, or a range determined by {@code
  57  * LayoutStyle}. The range for {@code Component}s is determined from
  58  * the {@code Component}'s {@code getMinimumSize}, {@code
  59  * getPreferredSize} and {@code getMaximumSize} methods. In addition,
  60  * when adding {@code Component}s you may specify a particular range
  61  * to use instead of that from the component. The range for a {@code
  62  * Group} is determined by the type of group. A {@code ParallelGroup}'s
  63  * range is the maximum of the ranges of its elements. A {@code
  64  * SequentialGroup}'s range is the sum of the ranges of its elements.
  65  * <p>
  66  * {@code GroupLayout} treats each axis independently.  That is, there
  67  * is a group representing the horizontal axis, and a group
  68  * representing the vertical axis.  The horizontal group is
  69  * responsible for determining the minimum, preferred and maximum size
  70  * along the horizontal axis as well as setting the x and width of the
  71  * components contained in it. The vertical group is responsible for
  72  * determining the minimum, preferred and maximum size along the
  73  * vertical axis as well as setting the y and height of the
  74  * components contained in it. Each {@code Component} must exist in both
  75  * a horizontal and vertical group, otherwise an {@code IllegalStateException}
  76  * is thrown during layout, or when the minimum, preferred or
  77  * maximum size is requested.
  78  * <p>
  79  * The following diagram shows a sequential group along the horizontal
  80  * axis. The sequential group contains three components. A parallel group
  81  * was used along the vertical axis.
  82  * <p style="text-align:center">
  83  * <img src="doc-files/groupLayout.1.gif" alt="Sequential group along the horizontal axis in three components">
  84  * <p>
  85  * To reinforce that each axis is treated independently the diagram shows
  86  * the range of each group and element along each axis. The
  87  * range of each component has been projected onto the axes,
  88  * and the groups are rendered in blue (horizontal) and red (vertical).
  89  * For readability there is a gap between each of the elements in the
  90  * sequential group.
  91  * <p>
  92  * The sequential group along the horizontal axis is rendered as a solid
  93  * blue line. Notice the sequential group is the sum of the children elements
  94  * it contains.
  95  * <p>
  96  * Along the vertical axis the parallel group is the maximum of the height
  97  * of each of the components. As all three components have the same height,
  98  * the parallel group has the same height.
  99  * <p>
 100  * The following diagram shows the same three components, but with the
 101  * parallel group along the horizontal axis and the sequential group along
 102  * the vertical axis.
 103  *
 104  * <p style="text-align:center">
 105  * <img src="doc-files/groupLayout.2.gif" alt="Sequential group along the vertical axis in three components">
 106  * <p>
 107  * As {@code c1} is the largest of the three components, the parallel
 108  * group is sized to {@code c1}. As {@code c2} and {@code c3} are smaller
 109  * than {@code c1} they are aligned based on the alignment specified
 110  * for the component (if specified) or the default alignment of the
 111  * parallel group. In the diagram {@code c2} and {@code c3} were created
 112  * with an alignment of {@code LEADING}. If the component orientation were
 113  * right-to-left then {@code c2} and {@code c3} would be positioned on
 114  * the opposite side.
 115  * <p>
 116  * The following diagram shows a sequential group along both the horizontal
 117  * and vertical axis.
 118  * <p style="text-align:center">
 119  * <img src="doc-files/groupLayout.3.gif" alt="Sequential group along both the horizontal and vertical axis in three components">
 120  * <p>
 121  * {@code GroupLayout} provides the ability to insert gaps between
 122  * {@code Component}s. The size of the gap is determined by an
 123  * instance of {@code LayoutStyle}. This may be turned on using the
 124  * {@code setAutoCreateGaps} method.  Similarly, you may use
 125  * the {@code setAutoCreateContainerGaps} method to insert gaps
 126  * between components that touch the edge of the parent container and the
 127  * container.
 128  * <p>
 129  * The following builds a panel consisting of two labels in
 130  * one column, followed by two textfields in the next column:
 131  * <pre>
 132  *   JComponent panel = ...;
 133  *   GroupLayout layout = new GroupLayout(panel);
 134  *   panel.setLayout(layout);
 135  *
 136  *   // Turn on automatically adding gaps between components
 137  *   layout.setAutoCreateGaps(true);
 138  *
 139  *   // Turn on automatically creating gaps between components that touch
 140  *   // the edge of the container and the container.
 141  *   layout.setAutoCreateContainerGaps(true);
 142  *
 143  *   // Create a sequential group for the horizontal axis.
 144  *
 145  *   GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
 146  *
 147  *   // The sequential group in turn contains two parallel groups.
 148  *   // One parallel group contains the labels, the other the text fields.
 149  *   // Putting the labels in a parallel group along the horizontal axis
 150  *   // positions them at the same x location.
 151  *   //
 152  *   // Variable indentation is used to reinforce the level of grouping.
 153  *   hGroup.addGroup(layout.createParallelGroup().
 154  *            addComponent(label1).addComponent(label2));
 155  *   hGroup.addGroup(layout.createParallelGroup().
 156  *            addComponent(tf1).addComponent(tf2));
 157  *   layout.setHorizontalGroup(hGroup);
 158  *
 159  *   // Create a sequential group for the vertical axis.
 160  *   GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
 161  *
 162  *   // The sequential group contains two parallel groups that align
 163  *   // the contents along the baseline. The first parallel group contains
 164  *   // the first label and text field, and the second parallel group contains
 165  *   // the second label and text field. By using a sequential group
 166  *   // the labels and text fields are positioned vertically after one another.
 167  *   vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
 168  *            addComponent(label1).addComponent(tf1));
 169  *   vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
 170  *            addComponent(label2).addComponent(tf2));
 171  *   layout.setVerticalGroup(vGroup);
 172  * </pre>
 173  * <p>
 174  * When run the following is produced.
 175  * <p style="text-align:center">
 176  * <img src="doc-files/groupLayout.example.png" alt="Produced horizontal/vertical form">
 177  * <p>
 178  * This layout consists of the following.
 179  * <ul><li>The horizontal axis consists of a sequential group containing two
 180  *         parallel groups.  The first parallel group contains the labels,
 181  *         and the second parallel group contains the text fields.
 182  *     <li>The vertical axis consists of a sequential group
 183  *         containing two parallel groups.  The parallel groups are configured
 184  *         to align their components along the baseline. The first parallel
 185  *         group contains the first label and first text field, and
 186  *         the second group consists of the second label and second
 187  *         text field.
 188  * </ul>
 189  * There are a couple of things to notice in this code:
 190  * <ul>
 191  *   <li>You need not explicitly add the components to the container; this
 192  *       is indirectly done by using one of the {@code add} methods of
 193  *       {@code Group}.
 194  *   <li>The various {@code add} methods return
 195  *       the caller.  This allows for easy chaining of invocations.  For
 196  *       example, {@code group.addComponent(label1).addComponent(label2);} is
 197  *       equivalent to
 198  *       {@code group.addComponent(label1); group.addComponent(label2);}.
 199  *   <li>There are no public constructors for {@code Group}s; instead
 200  *       use the create methods of {@code GroupLayout}.
 201  * </ul>
 202  *
 203  * @author Tomas Pavek
 204  * @author Jan Stola
 205  * @author Scott Violet
 206  * @since 1.6
 207  */
 208 public class GroupLayout implements LayoutManager2 {
 209     // Used in size calculations
 210     private static final int MIN_SIZE = 0;
 211 
 212     private static final int PREF_SIZE = 1;
 213 
 214     private static final int MAX_SIZE = 2;
 215 
 216     // Used by prepare, indicates min, pref or max isn't going to be used.
 217     private static final int SPECIFIC_SIZE = 3;
 218 
 219     private static final int UNSET = Integer.MIN_VALUE;
 220 
 221     // Maximum spring size constrain to avoid integer overflow
 222     private static final int INFINITE = Integer.MAX_VALUE >> 1;
 223 
 224     /**
 225      * Indicates the size from the component or gap should be used for a
 226      * particular range value.
 227      *
 228      * @see Group
 229      */
 230     public static final int DEFAULT_SIZE = -1;
 231 
 232     /**
 233      * Indicates the preferred size from the component or gap should
 234      * be used for a particular range value.
 235      *
 236      * @see Group
 237      */
 238     public static final int PREFERRED_SIZE = -2;
 239 
 240     // Whether or not we automatically try and create the preferred
 241     // padding between components.
 242     private boolean autocreatePadding;
 243 
 244     // Whether or not we automatically try and create the preferred
 245     // padding between components the touch the edge of the container and
 246     // the container.
 247     private boolean autocreateContainerPadding;
 248 
 249     /**
 250      * Group responsible for layout along the horizontal axis.  This is NOT
 251      * the user specified group, use getHorizontalGroup to dig that out.
 252      */
 253     private Group horizontalGroup;
 254 
 255     /**
 256      * Group responsible for layout along the vertical axis.  This is NOT
 257      * the user specified group, use getVerticalGroup to dig that out.
 258      */
 259     private Group verticalGroup;
 260 
 261     // Maps from Component to ComponentInfo.  This is used for tracking
 262     // information specific to a Component.
 263     private Map<Component,ComponentInfo> componentInfos;
 264 
 265     // Container we're doing layout for.
 266     private Container host;
 267 
 268     // Used by areParallelSiblings, cached to avoid excessive garbage.
 269     private Set<Spring> tmpParallelSet;
 270 
 271     // Indicates Springs have changed in some way since last change.
 272     private boolean springsChanged;
 273 
 274     // Indicates invalidateLayout has been invoked.
 275     private boolean isValid;
 276 
 277     // Whether or not any preferred padding (or container padding) springs
 278     // exist
 279     private boolean hasPreferredPaddingSprings;
 280 
 281     /**
 282      * The LayoutStyle instance to use, if null the sharedInstance is used.
 283      */
 284     private LayoutStyle layoutStyle;
 285 
 286     /**
 287      * If true, components that are not visible are treated as though they
 288      * aren't there.
 289      */
 290     private boolean honorsVisibility;
 291 
 292 
 293     /**
 294      * Enumeration of the possible ways {@code ParallelGroup} can align
 295      * its children.
 296      *
 297      * @see #createParallelGroup(Alignment)
 298      * @since 1.6
 299      */
 300     public enum Alignment {
 301         /**
 302          * Indicates the elements should be
 303          * aligned to the origin.  For the horizontal axis with a left to
 304          * right orientation this means aligned to the left edge. For the
 305          * vertical axis leading means aligned to the top edge.
 306          *
 307          * @see #createParallelGroup(Alignment)
 308          */
 309         LEADING,
 310 
 311         /**
 312          * Indicates the elements should be aligned to the end of the
 313          * region.  For the horizontal axis with a left to right
 314          * orientation this means aligned to the right edge. For the
 315          * vertical axis trailing means aligned to the bottom edge.
 316          *
 317          * @see #createParallelGroup(Alignment)
 318          */
 319         TRAILING,
 320 
 321         /**
 322          * Indicates the elements should be centered in
 323          * the region.
 324          *
 325          * @see #createParallelGroup(Alignment)
 326          */
 327         CENTER,
 328 
 329         /**
 330          * Indicates the elements should be aligned along
 331          * their baseline.
 332          *
 333          * @see #createParallelGroup(Alignment)
 334          * @see #createBaselineGroup(boolean,boolean)
 335          */
 336         BASELINE
 337     }
 338 
 339 
 340     private static void checkSize(int min, int pref, int max,
 341             boolean isComponentSpring) {
 342         checkResizeType(min, isComponentSpring);
 343         if (!isComponentSpring && pref < 0) {
 344             throw new IllegalArgumentException("Pref must be >= 0");
 345         } else if (isComponentSpring) {
 346             checkResizeType(pref, true);
 347         }
 348         checkResizeType(max, isComponentSpring);
 349         checkLessThan(min, pref);
 350         checkLessThan(pref, max);
 351     }
 352 
 353     private static void checkResizeType(int type, boolean isComponentSpring) {
 354         if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE &&
 355                 type != PREFERRED_SIZE) ||
 356                 (!isComponentSpring && type != PREFERRED_SIZE))) {
 357             throw new IllegalArgumentException("Invalid size");
 358         }
 359     }
 360 
 361     private static void checkLessThan(int min, int max) {
 362         if (min >= 0 && max >= 0 && min > max) {
 363             throw new IllegalArgumentException(
 364                     "Following is not met: min<=pref<=max");
 365         }
 366     }
 367 
 368     /**
 369      * Creates a {@code GroupLayout} for the specified {@code Container}.
 370      *
 371      * @param host the {@code Container} the {@code GroupLayout} is
 372      *        the {@code LayoutManager} for
 373      * @throws IllegalArgumentException if host is {@code null}
 374      */
 375     public GroupLayout(Container host) {
 376         if (host == null) {
 377             throw new IllegalArgumentException("Container must be non-null");
 378         }
 379         honorsVisibility = true;
 380         this.host = host;
 381         setHorizontalGroup(createParallelGroup(Alignment.LEADING, true));
 382         setVerticalGroup(createParallelGroup(Alignment.LEADING, true));
 383         componentInfos = new HashMap<Component,ComponentInfo>();
 384         tmpParallelSet = new HashSet<Spring>();
 385     }
 386 
 387     /**
 388      * Sets whether component visibility is considered when sizing and
 389      * positioning components. A value of {@code true} indicates that
 390      * non-visible components should not be treated as part of the
 391      * layout. A value of {@code false} indicates that components should be
 392      * positioned and sized regardless of visibility.
 393      * <p>
 394      * A value of {@code false} is useful when the visibility of components
 395      * is dynamically adjusted and you don't want surrounding components and
 396      * the sizing to change.
 397      * <p>
 398      * The specified value is used for components that do not have an
 399      * explicit visibility specified.
 400      * <p>
 401      * The default is {@code true}.
 402      *
 403      * @param honorsVisibility whether component visibility is considered when
 404      *                         sizing and positioning components
 405      * @see #setHonorsVisibility(Component,Boolean)
 406      */
 407     public void setHonorsVisibility(boolean honorsVisibility) {
 408         if (this.honorsVisibility != honorsVisibility) {
 409             this.honorsVisibility = honorsVisibility;
 410             springsChanged = true;
 411             isValid = false;
 412             invalidateHost();
 413         }
 414     }
 415 
 416     /**
 417      * Returns whether component visibility is considered when sizing and
 418      * positioning components.
 419      *
 420      * @return whether component visibility is considered when sizing and
 421      *         positioning components
 422      */
 423     public boolean getHonorsVisibility() {
 424         return honorsVisibility;
 425     }
 426 
 427     /**
 428      * Sets whether the component's visibility is considered for
 429      * sizing and positioning. A value of {@code Boolean.TRUE}
 430      * indicates that if {@code component} is not visible it should
 431      * not be treated as part of the layout. A value of {@code false}
 432      * indicates that {@code component} is positioned and sized
 433      * regardless of it's visibility.  A value of {@code null}
 434      * indicates the value specified by the single argument method {@code
 435      * setHonorsVisibility} should be used.
 436      * <p>
 437      * If {@code component} is not a child of the {@code Container} this
 438      * {@code GroupLayout} is managing, it will be added to the
 439      * {@code Container}.
 440      *
 441      * @param component the component
 442      * @param honorsVisibility whether visibility of this {@code component} should be
 443      *              considered for sizing and positioning
 444      * @throws IllegalArgumentException if {@code component} is {@code null}
 445      * @see #setHonorsVisibility(Component,Boolean)
 446      */
 447     public void setHonorsVisibility(Component component,
 448             Boolean honorsVisibility) {
 449         if (component == null) {
 450             throw new IllegalArgumentException("Component must be non-null");
 451         }
 452         getComponentInfo(component).setHonorsVisibility(honorsVisibility);
 453         springsChanged = true;
 454         isValid = false;
 455         invalidateHost();
 456     }
 457 
 458     /**
 459      * Sets whether a gap between components should automatically be
 460      * created.  For example, if this is {@code true} and you add two
 461      * components to a {@code SequentialGroup} a gap between the
 462      * two components is automatically be created.  The default is
 463      * {@code false}.
 464      *
 465      * @param autoCreatePadding whether a gap between components is
 466      *        automatically created
 467      */
 468     public void setAutoCreateGaps(boolean autoCreatePadding) {
 469         if (this.autocreatePadding != autoCreatePadding) {
 470             this.autocreatePadding = autoCreatePadding;
 471             invalidateHost();
 472         }
 473     }
 474 
 475     /**
 476      * Returns {@code true} if gaps between components are automatically
 477      * created.
 478      *
 479      * @return {@code true} if gaps between components are automatically
 480      *         created
 481      */
 482     public boolean getAutoCreateGaps() {
 483         return autocreatePadding;
 484     }
 485 
 486     /**
 487      * Sets whether a gap between the container and components that
 488      * touch the border of the container should automatically be
 489      * created. The default is {@code false}.
 490      *
 491      * @param autoCreateContainerPadding whether a gap between the container and
 492      *        components that touch the border of the container should
 493      *        automatically be created
 494      */
 495     public void setAutoCreateContainerGaps(boolean autoCreateContainerPadding){
 496         if (this.autocreateContainerPadding != autoCreateContainerPadding) {
 497             this.autocreateContainerPadding = autoCreateContainerPadding;
 498             horizontalGroup = createTopLevelGroup(getHorizontalGroup());
 499             verticalGroup = createTopLevelGroup(getVerticalGroup());
 500             invalidateHost();
 501         }
 502     }
 503 
 504     /**
 505      * Returns {@code true} if gaps between the container and components that
 506      * border the container are automatically created.
 507      *
 508      * @return {@code true} if gaps between the container and components that
 509      *         border the container are automatically created
 510      */
 511     public boolean getAutoCreateContainerGaps() {
 512         return autocreateContainerPadding;
 513     }
 514 
 515     /**
 516      * Sets the {@code Group} that positions and sizes
 517      * components along the horizontal axis.
 518      *
 519      * @param group the {@code Group} that positions and sizes
 520      *        components along the horizontal axis
 521      * @throws IllegalArgumentException if group is {@code null}
 522      */
 523     public void setHorizontalGroup(Group group) {
 524         if (group == null) {
 525             throw new IllegalArgumentException("Group must be non-null");
 526         }
 527         horizontalGroup = createTopLevelGroup(group);
 528         invalidateHost();
 529     }
 530 
 531     /**
 532      * Returns the {@code Group} that positions and sizes components
 533      * along the horizontal axis.
 534      *
 535      * @return the {@code Group} responsible for positioning and
 536      *         sizing component along the horizontal axis
 537      */
 538     private Group getHorizontalGroup() {
 539         int index = 0;
 540         if (horizontalGroup.springs.size() > 1) {
 541             index = 1;
 542         }
 543         return (Group)horizontalGroup.springs.get(index);
 544     }
 545 
 546     /**
 547      * Sets the {@code Group} that positions and sizes
 548      * components along the vertical axis.
 549      *
 550      * @param group the {@code Group} that positions and sizes
 551      *        components along the vertical axis
 552      * @throws IllegalArgumentException if group is {@code null}
 553      */
 554     public void setVerticalGroup(Group group) {
 555         if (group == null) {
 556             throw new IllegalArgumentException("Group must be non-null");
 557         }
 558         verticalGroup = createTopLevelGroup(group);
 559         invalidateHost();
 560     }
 561 
 562     /**
 563      * Returns the {@code Group} that positions and sizes components
 564      * along the vertical axis.
 565      *
 566      * @return the {@code Group} responsible for positioning and
 567      *         sizing component along the vertical axis
 568      */
 569     private Group getVerticalGroup() {
 570         int index = 0;
 571         if (verticalGroup.springs.size() > 1) {
 572             index = 1;
 573         }
 574         return (Group)verticalGroup.springs.get(index);
 575     }
 576 
 577     /**
 578      * Wraps the user specified group in a sequential group.  If
 579      * container gaps should be generated the necessary springs are
 580      * added.
 581      */
 582     private Group createTopLevelGroup(Group specifiedGroup) {
 583         SequentialGroup group = createSequentialGroup();
 584         if (getAutoCreateContainerGaps()) {
 585             group.addSpring(new ContainerAutoPreferredGapSpring());
 586             group.addGroup(specifiedGroup);
 587             group.addSpring(new ContainerAutoPreferredGapSpring());
 588         } else {
 589             group.addGroup(specifiedGroup);
 590         }
 591         return group;
 592     }
 593 
 594     /**
 595      * Creates and returns a {@code SequentialGroup}.
 596      *
 597      * @return a new {@code SequentialGroup}
 598      */
 599     public SequentialGroup createSequentialGroup() {
 600         return new SequentialGroup();
 601     }
 602 
 603     /**
 604      * Creates and returns a {@code ParallelGroup} with an alignment of
 605      * {@code Alignment.LEADING}.  This is a cover method for the more
 606      * general {@code createParallelGroup(Alignment)} method.
 607      *
 608      * @return a new {@code ParallelGroup}
 609      * @see #createParallelGroup(Alignment)
 610      */
 611     public ParallelGroup createParallelGroup() {
 612         return createParallelGroup(Alignment.LEADING);
 613     }
 614 
 615     /**
 616      * Creates and returns a {@code ParallelGroup} with the specified
 617      * alignment.  This is a cover method for the more general {@code
 618      * createParallelGroup(Alignment,boolean)} method with {@code true}
 619      * supplied for the second argument.
 620      *
 621      * @param alignment the alignment for the elements of the group
 622      * @throws IllegalArgumentException if {@code alignment} is {@code null}
 623      * @return a new {@code ParallelGroup}
 624      * @see #createBaselineGroup
 625      * @see ParallelGroup
 626      */
 627     public ParallelGroup createParallelGroup(Alignment alignment) {
 628         return createParallelGroup(alignment, true);
 629     }
 630 
 631     /**
 632      * Creates and returns a {@code ParallelGroup} with the specified
 633      * alignment and resize behavior. The {@code
 634      * alignment} argument specifies how children elements are
 635      * positioned that do not fill the group. For example, if a {@code
 636      * ParallelGroup} with an alignment of {@code TRAILING} is given
 637      * 100 and a child only needs 50, the child is
 638      * positioned at the position 50 (with a component orientation of
 639      * left-to-right).
 640      * <p>
 641      * Baseline alignment is only useful when used along the vertical
 642      * axis. A {@code ParallelGroup} created with a baseline alignment
 643      * along the horizontal axis is treated as {@code LEADING}.
 644      * <p>
 645      * Refer to {@link GroupLayout.ParallelGroup ParallelGroup} for details on
 646      * the behavior of baseline groups.
 647      *
 648      * @param alignment the alignment for the elements of the group
 649      * @param resizable {@code true} if the group is resizable; if the group
 650      *        is not resizable the preferred size is used for the
 651      *        minimum and maximum size of the group
 652      * @throws IllegalArgumentException if {@code alignment} is {@code null}
 653      * @return a new {@code ParallelGroup}
 654      * @see #createBaselineGroup
 655      * @see GroupLayout.ParallelGroup
 656      */
 657     public ParallelGroup createParallelGroup(Alignment alignment,
 658             boolean resizable){
 659         if (alignment == null) {
 660             throw new IllegalArgumentException("alignment must be non null");
 661         }
 662 
 663         if (alignment == Alignment.BASELINE) {
 664             return new BaselineGroup(resizable);
 665         }
 666         return new ParallelGroup(alignment, resizable);
 667     }
 668 
 669     /**
 670      * Creates and returns a {@code ParallelGroup} that aligns it's
 671      * elements along the baseline.
 672      *
 673      * @param resizable whether the group is resizable
 674      * @param anchorBaselineToTop whether the baseline is anchored to
 675      *        the top or bottom of the group
 676      * @return the {@code ParallelGroup}
 677      * @see #createBaselineGroup
 678      * @see ParallelGroup
 679      */
 680     public ParallelGroup createBaselineGroup(boolean resizable,
 681             boolean anchorBaselineToTop) {
 682         return new BaselineGroup(resizable, anchorBaselineToTop);
 683     }
 684 
 685     /**
 686      * Forces the specified components to have the same size
 687      * regardless of their preferred, minimum or maximum sizes. Components that
 688      * are linked are given the maximum of the preferred size of each of
 689      * the linked components. For example, if you link two components with
 690      * a preferred width of 10 and 20, both components are given a width of 20.
 691      * <p>
 692      * This can be used multiple times to force any number of
 693      * components to share the same size.
 694      * <p>
 695      * Linked Components are not be resizable.
 696      *
 697      * @param components the {@code Component}s that are to have the same size
 698      * @throws IllegalArgumentException if {@code components} is
 699      *         {@code null}, or contains {@code null}
 700      * @see #linkSize(int,Component[])
 701      */
 702     public void linkSize(Component... components) {
 703         linkSize(SwingConstants.HORIZONTAL, components);
 704         linkSize(SwingConstants.VERTICAL, components);
 705     }
 706 
 707     /**
 708      * Forces the specified components to have the same size along the
 709      * specified axis regardless of their preferred, minimum or
 710      * maximum sizes. Components that are linked are given the maximum
 711      * of the preferred size of each of the linked components. For
 712      * example, if you link two components along the horizontal axis
 713      * and the preferred width is 10 and 20, both components are given
 714      * a width of 20.
 715      * <p>
 716      * This can be used multiple times to force any number of
 717      * components to share the same size.
 718      * <p>
 719      * Linked {@code Component}s are not be resizable.
 720      *
 721      * @param components the {@code Component}s that are to have the same size
 722      * @param axis the axis to link the size along; one of
 723      *             {@code SwingConstants.HORIZONTAL} or
 724      *             {@code SwingConstans.VERTICAL}
 725      * @throws IllegalArgumentException if {@code components} is
 726      *         {@code null}, or contains {@code null}; or {@code axis}
 727      *          is not {@code SwingConstants.HORIZONTAL} or
 728      *          {@code SwingConstants.VERTICAL}
 729      */
 730     public void linkSize(int axis, Component... components) {
 731         if (components == null) {
 732             throw new IllegalArgumentException("Components must be non-null");
 733         }
 734         for (int counter = components.length - 1; counter >= 0; counter--) {
 735             Component c = components[counter];
 736             if (components[counter] == null) {
 737                 throw new IllegalArgumentException(
 738                         "Components must be non-null");
 739             }
 740             // Force the component to be added
 741             getComponentInfo(c);
 742         }
 743         int glAxis;
 744         if (axis == SwingConstants.HORIZONTAL) {
 745             glAxis = HORIZONTAL;
 746         } else if (axis == SwingConstants.VERTICAL) {
 747             glAxis = VERTICAL;
 748         } else {
 749             throw new IllegalArgumentException("Axis must be one of " +
 750                     "SwingConstants.HORIZONTAL or SwingConstants.VERTICAL");
 751         }
 752         LinkInfo master = getComponentInfo(
 753                 components[components.length - 1]).getLinkInfo(glAxis);
 754         for (int counter = components.length - 2; counter >= 0; counter--) {
 755             master.add(getComponentInfo(components[counter]));
 756         }
 757         invalidateHost();
 758     }
 759 
 760     /**
 761      * Replaces an existing component with a new one.
 762      *
 763      * @param existingComponent the component that should be removed
 764      *        and replaced with {@code newComponent}
 765      * @param newComponent the component to put in
 766      *        {@code existingComponent}'s place
 767      * @throws IllegalArgumentException if either of the components are
 768      *         {@code null} or {@code existingComponent} is not being managed
 769      *         by this layout manager
 770      */
 771     public void replace(Component existingComponent, Component newComponent) {
 772         if (existingComponent == null || newComponent == null) {
 773             throw new IllegalArgumentException("Components must be non-null");
 774         }
 775         // Make sure all the components have been registered, otherwise we may
 776         // not update the correct Springs.
 777         if (springsChanged) {
 778             registerComponents(horizontalGroup, HORIZONTAL);
 779             registerComponents(verticalGroup, VERTICAL);
 780         }
 781         ComponentInfo info = componentInfos.remove(existingComponent);
 782         if (info == null) {
 783             throw new IllegalArgumentException("Component must already exist");
 784         }
 785         host.remove(existingComponent);
 786         if (newComponent.getParent() != host) {
 787             host.add(newComponent);
 788         }
 789         info.setComponent(newComponent);
 790         componentInfos.put(newComponent, info);
 791         invalidateHost();
 792     }
 793 
 794     /**
 795      * Sets the {@code LayoutStyle} used to calculate the preferred
 796      * gaps between components. A value of {@code null} indicates the
 797      * shared instance of {@code LayoutStyle} should be used.
 798      *
 799      * @param layoutStyle the {@code LayoutStyle} to use
 800      * @see LayoutStyle
 801      */
 802     public void setLayoutStyle(LayoutStyle layoutStyle) {
 803         this.layoutStyle = layoutStyle;
 804         invalidateHost();
 805     }
 806 
 807     /**
 808      * Returns the {@code LayoutStyle} used for calculating the preferred
 809      * gap between components. This returns the value specified to
 810      * {@code setLayoutStyle}, which may be {@code null}.
 811      *
 812      * @return the {@code LayoutStyle} used for calculating the preferred
 813      *         gap between components
 814      */
 815     public LayoutStyle getLayoutStyle() {
 816         return layoutStyle;
 817     }
 818 
 819     private LayoutStyle getLayoutStyle0() {
 820         LayoutStyle layoutStyle = getLayoutStyle();
 821         if (layoutStyle == null) {
 822             layoutStyle = LayoutStyle.getInstance();
 823         }
 824         return layoutStyle;
 825     }
 826 
 827     private void invalidateHost() {
 828         if (host instanceof JComponent) {
 829             ((JComponent)host).revalidate();
 830         } else {
 831             host.invalidate();
 832         }
 833         host.repaint();
 834     }
 835 
 836     //
 837     // LayoutManager
 838     //
 839     /**
 840      * Notification that a {@code Component} has been added to
 841      * the parent container.  You should not invoke this method
 842      * directly, instead you should use one of the {@code Group}
 843      * methods to add a {@code Component}.
 844      *
 845      * @param name the string to be associated with the component
 846      * @param component the {@code Component} to be added
 847      */
 848     public void addLayoutComponent(String name, Component component) {
 849     }
 850 
 851     /**
 852      * Notification that a {@code Component} has been removed from
 853      * the parent container.  You should not invoke this method
 854      * directly, instead invoke {@code remove} on the parent
 855      * {@code Container}.
 856      *
 857      * @param component the component to be removed
 858      * @see java.awt.Component#remove
 859      */
 860     public void removeLayoutComponent(Component component) {
 861         ComponentInfo info = componentInfos.remove(component);
 862         if (info != null) {
 863             info.dispose();
 864             springsChanged = true;
 865             isValid = false;
 866         }
 867     }
 868 
 869     /**
 870      * Returns the preferred size for the specified container.
 871      *
 872      * @param parent the container to return the preferred size for
 873      * @return the preferred size for {@code parent}
 874      * @throws IllegalArgumentException if {@code parent} is not
 875      *         the same {@code Container} this was created with
 876      * @throws IllegalStateException if any of the components added to
 877      *         this layout are not in both a horizontal and vertical group
 878      * @see java.awt.Container#getPreferredSize
 879      */
 880     public Dimension preferredLayoutSize(Container parent) {
 881         checkParent(parent);
 882         prepare(PREF_SIZE);
 883         return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),
 884                 verticalGroup.getPreferredSize(VERTICAL));
 885     }
 886 
 887     /**
 888      * Returns the minimum size for the specified container.
 889      *
 890      * @param parent the container to return the size for
 891      * @return the minimum size for {@code parent}
 892      * @throws IllegalArgumentException if {@code parent} is not
 893      *         the same {@code Container} that this was created with
 894      * @throws IllegalStateException if any of the components added to
 895      *         this layout are not in both a horizontal and vertical group
 896      * @see java.awt.Container#getMinimumSize
 897      */
 898     public Dimension minimumLayoutSize(Container parent) {
 899         checkParent(parent);
 900         prepare(MIN_SIZE);
 901         return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),
 902                 verticalGroup.getMinimumSize(VERTICAL));
 903     }
 904 
 905     /**
 906      * Lays out the specified container.
 907      *
 908      * @param parent the container to be laid out
 909      * @throws IllegalStateException if any of the components added to
 910      *         this layout are not in both a horizontal and vertical group
 911      */
 912     public void layoutContainer(Container parent) {
 913         // Step 1: Prepare for layout.
 914         prepare(SPECIFIC_SIZE);
 915         Insets insets = parent.getInsets();
 916         int width = parent.getWidth() - insets.left - insets.right;
 917         int height = parent.getHeight() - insets.top - insets.bottom;
 918         boolean ltr = isLeftToRight();
 919         if (getAutoCreateGaps() || getAutoCreateContainerGaps() ||
 920                 hasPreferredPaddingSprings) {
 921             // Step 2: Calculate autopadding springs
 922             calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0,
 923                     width);
 924             calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0,
 925                     height);
 926         }
 927         // Step 3: set the size of the groups.
 928         horizontalGroup.setSize(HORIZONTAL, 0, width);
 929         verticalGroup.setSize(VERTICAL, 0, height);
 930         // Step 4: apply the size to the components.
 931         for (ComponentInfo info : componentInfos.values()) {
 932             info.setBounds(insets, width, ltr);
 933         }
 934     }
 935 
 936     //
 937     // LayoutManager2
 938     //
 939     /**
 940      * Notification that a {@code Component} has been added to
 941      * the parent container.  You should not invoke this method
 942      * directly, instead you should use one of the {@code Group}
 943      * methods to add a {@code Component}.
 944      *
 945      * @param component the component added
 946      * @param constraints description of where to place the component
 947      */
 948     public void addLayoutComponent(Component component, Object constraints) {
 949     }
 950 
 951     /**
 952      * Returns the maximum size for the specified container.
 953      *
 954      * @param parent the container to return the size for
 955      * @return the maximum size for {@code parent}
 956      * @throws IllegalArgumentException if {@code parent} is not
 957      *         the same {@code Container} that this was created with
 958      * @throws IllegalStateException if any of the components added to
 959      *         this layout are not in both a horizontal and vertical group
 960      * @see java.awt.Container#getMaximumSize
 961      */
 962     public Dimension maximumLayoutSize(Container parent) {
 963         checkParent(parent);
 964         prepare(MAX_SIZE);
 965         return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),
 966                 verticalGroup.getMaximumSize(VERTICAL));
 967     }
 968 
 969     /**
 970      * Returns the alignment along the x axis.  This specifies how
 971      * the component would like to be aligned relative to other
 972      * components.  The value should be a number between 0 and 1
 973      * where 0 represents alignment along the origin, 1 is aligned
 974      * the furthest away from the origin, 0.5 is centered, etc.
 975      *
 976      * @param parent the {@code Container} hosting this {@code LayoutManager}
 977      * @throws IllegalArgumentException if {@code parent} is not
 978      *         the same {@code Container} that this was created with
 979      * @return the alignment; this implementation returns {@code .5}
 980      */
 981     public float getLayoutAlignmentX(Container parent) {
 982         checkParent(parent);
 983         return .5f;
 984     }
 985 
 986     /**
 987      * Returns the alignment along the y axis.  This specifies how
 988      * the component would like to be aligned relative to other
 989      * components.  The value should be a number between 0 and 1
 990      * where 0 represents alignment along the origin, 1 is aligned
 991      * the furthest away from the origin, 0.5 is centered, etc.
 992      *
 993      * @param parent the {@code Container} hosting this {@code LayoutManager}
 994      * @throws IllegalArgumentException if {@code parent} is not
 995      *         the same {@code Container} that this was created with
 996      * @return alignment; this implementation returns {@code .5}
 997      */
 998     public float getLayoutAlignmentY(Container parent) {
 999         checkParent(parent);
1000         return .5f;
1001     }
1002 
1003     /**
1004      * Invalidates the layout, indicating that if the layout manager
1005      * has cached information it should be discarded.
1006      *
1007      * @param parent the {@code Container} hosting this LayoutManager
1008      * @throws IllegalArgumentException if {@code parent} is not
1009      *         the same {@code Container} that this was created with
1010      */
1011     public void invalidateLayout(Container parent) {
1012         checkParent(parent);
1013         // invalidateLayout is called from Container.invalidate, which
1014         // does NOT grab the treelock.  All other methods do.  To make sure
1015         // there aren't any possible threading problems we grab the tree lock
1016         // here.
1017         synchronized(parent.getTreeLock()) {
1018             isValid = false;
1019         }
1020     }
1021 
1022     private void prepare(int sizeType) {
1023         boolean visChanged = false;
1024         // Step 1: If not-valid, clear springs and update visibility.
1025         if (!isValid) {
1026             isValid = true;
1027             horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);
1028             verticalGroup.setSize(VERTICAL, UNSET, UNSET);
1029             for (ComponentInfo ci : componentInfos.values()) {
1030                 if (ci.updateVisibility()) {
1031                     visChanged = true;
1032                 }
1033                 ci.clearCachedSize();
1034             }
1035         }
1036         // Step 2: Make sure components are bound to ComponentInfos
1037         if (springsChanged) {
1038             registerComponents(horizontalGroup, HORIZONTAL);
1039             registerComponents(verticalGroup, VERTICAL);
1040         }
1041         // Step 3: Adjust the autopadding. This removes existing
1042         // autopadding, then recalculates where it should go.
1043         if (springsChanged || visChanged) {
1044             checkComponents();
1045             horizontalGroup.removeAutopadding();
1046             verticalGroup.removeAutopadding();
1047             if (getAutoCreateGaps()) {
1048                 insertAutopadding(true);
1049             } else if (hasPreferredPaddingSprings ||
1050                     getAutoCreateContainerGaps()) {
1051                 insertAutopadding(false);
1052             }
1053             springsChanged = false;
1054         }
1055         // Step 4: (for min/pref/max size calculations only) calculate the
1056         // autopadding. This invokes for unsetting the calculated values, then
1057         // recalculating them.
1058         // If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this
1059         // step will be done later on.
1060         if (sizeType != SPECIFIC_SIZE && (getAutoCreateGaps() ||
1061                 getAutoCreateContainerGaps() || hasPreferredPaddingSprings)) {
1062             calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0);
1063             calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);
1064         }
1065     }
1066 
1067     private void calculateAutopadding(Group group, int axis, int sizeType,
1068             int origin, int size) {
1069         group.unsetAutopadding();
1070         switch(sizeType) {
1071             case MIN_SIZE:
1072                 size = group.getMinimumSize(axis);
1073                 break;
1074             case PREF_SIZE:
1075                 size = group.getPreferredSize(axis);
1076                 break;
1077             case MAX_SIZE:
1078                 size = group.getMaximumSize(axis);
1079                 break;
1080             default:
1081                 break;
1082         }
1083         group.setSize(axis, origin, size);
1084         group.calculateAutopadding(axis);
1085     }
1086 
1087     private void checkComponents() {
1088         for (ComponentInfo info : componentInfos.values()) {
1089             if (info.horizontalSpring == null) {
1090                 throw new IllegalStateException(info.component +
1091                         " is not attached to a horizontal group");
1092             }
1093             if (info.verticalSpring == null) {
1094                 throw new IllegalStateException(info.component +
1095                         " is not attached to a vertical group");
1096             }
1097         }
1098     }
1099 
1100     private void registerComponents(Group group, int axis) {
1101         List<Spring> springs = group.springs;
1102         for (int counter = springs.size() - 1; counter >= 0; counter--) {
1103             Spring spring = springs.get(counter);
1104             if (spring instanceof ComponentSpring) {
1105                 ((ComponentSpring)spring).installIfNecessary(axis);
1106             } else if (spring instanceof Group) {
1107                 registerComponents((Group)spring, axis);
1108             }
1109         }
1110     }
1111 
1112     private Dimension adjustSize(int width, int height) {
1113         Insets insets = host.getInsets();
1114         return new Dimension(width + insets.left + insets.right,
1115                 height + insets.top + insets.bottom);
1116     }
1117 
1118     private void checkParent(Container parent) {
1119         if (parent != host) {
1120             throw new IllegalArgumentException(
1121                     "GroupLayout can only be used with one Container at a time");
1122         }
1123     }
1124 
1125     /**
1126      * Returns the {@code ComponentInfo} for the specified Component,
1127      * creating one if necessary.
1128      */
1129     private ComponentInfo getComponentInfo(Component component) {
1130         ComponentInfo info = componentInfos.get(component);
1131         if (info == null) {
1132             info = new ComponentInfo(component);
1133             componentInfos.put(component, info);
1134             if (component.getParent() != host) {
1135                 host.add(component);
1136             }
1137         }
1138         return info;
1139     }
1140 
1141     /**
1142      * Adjusts the autopadding springs for the horizontal and vertical
1143      * groups.  If {@code insert} is {@code true} this will insert auto padding
1144      * springs, otherwise this will only adjust the springs that
1145      * comprise auto preferred padding springs.
1146      */
1147     private void insertAutopadding(boolean insert) {
1148         horizontalGroup.insertAutopadding(HORIZONTAL,
1149                 new ArrayList<AutoPreferredGapSpring>(1),
1150                 new ArrayList<AutoPreferredGapSpring>(1),
1151                 new ArrayList<ComponentSpring>(1),
1152                 new ArrayList<ComponentSpring>(1), insert);
1153         verticalGroup.insertAutopadding(VERTICAL,
1154                 new ArrayList<AutoPreferredGapSpring>(1),
1155                 new ArrayList<AutoPreferredGapSpring>(1),
1156                 new ArrayList<ComponentSpring>(1),
1157                 new ArrayList<ComponentSpring>(1), insert);
1158     }
1159 
1160     /**
1161      * Returns {@code true} if the two Components have a common ParallelGroup
1162      * ancestor along the particular axis.
1163      */
1164     private boolean areParallelSiblings(Component source, Component target,
1165             int axis) {
1166         ComponentInfo sourceInfo = getComponentInfo(source);
1167         ComponentInfo targetInfo = getComponentInfo(target);
1168         Spring sourceSpring;
1169         Spring targetSpring;
1170         if (axis == HORIZONTAL) {
1171             sourceSpring = sourceInfo.horizontalSpring;
1172             targetSpring = targetInfo.horizontalSpring;
1173         } else {
1174             sourceSpring = sourceInfo.verticalSpring;
1175             targetSpring = targetInfo.verticalSpring;
1176         }
1177         Set<Spring> sourcePath = tmpParallelSet;
1178         sourcePath.clear();
1179         Spring spring = sourceSpring.getParent();
1180         while (spring != null) {
1181             sourcePath.add(spring);
1182             spring = spring.getParent();
1183         }
1184         spring = targetSpring.getParent();
1185         while (spring != null) {
1186             if (sourcePath.contains(spring)) {
1187                 sourcePath.clear();
1188                 while (spring != null) {
1189                     if (spring instanceof ParallelGroup) {
1190                         return true;
1191                     }
1192                     spring = spring.getParent();
1193                 }
1194                 return false;
1195             }
1196             spring = spring.getParent();
1197         }
1198         sourcePath.clear();
1199         return false;
1200     }
1201 
1202     private boolean isLeftToRight() {
1203         return host.getComponentOrientation().isLeftToRight();
1204     }
1205 
1206     /**
1207      * Returns a string representation of this {@code GroupLayout}.
1208      * This method is intended to be used for debugging purposes,
1209      * and the content and format of the returned string may vary
1210      * between implementations.
1211      *
1212      * @return a string representation of this {@code GroupLayout}
1213      **/
1214     public String toString() {
1215         if (springsChanged) {
1216             registerComponents(horizontalGroup, HORIZONTAL);
1217             registerComponents(verticalGroup, VERTICAL);
1218         }
1219         StringBuilder sb = new StringBuilder();
1220         sb.append("HORIZONTAL\n");
1221         createSpringDescription(sb, horizontalGroup, "  ", HORIZONTAL);
1222         sb.append("\nVERTICAL\n");
1223         createSpringDescription(sb, verticalGroup, "  ", VERTICAL);
1224         return sb.toString();
1225     }
1226 
1227     private void createSpringDescription(StringBuilder sb, Spring spring,
1228             String indent, int axis) {
1229         String origin = "";
1230         String padding = "";
1231         if (spring instanceof ComponentSpring) {
1232             ComponentSpring cSpring = (ComponentSpring)spring;
1233             origin = Integer.toString(cSpring.getOrigin()) + " ";
1234             String name = cSpring.getComponent().getName();
1235             if (name != null) {
1236                 origin = "name=" + name + ", ";
1237             }
1238         }
1239         if (spring instanceof AutoPreferredGapSpring) {
1240             AutoPreferredGapSpring paddingSpring =
1241                     (AutoPreferredGapSpring)spring;
1242             padding = ", userCreated=" + paddingSpring.getUserCreated() +
1243                     ", matches=" + paddingSpring.getMatchDescription();
1244         }
1245         sb.append(indent).append(spring.getClass().getName()).append(' ')
1246                 .append(Integer.toHexString(spring.hashCode())).append(' ')
1247                 .append(origin).append(", size=").append(spring.getSize())
1248                 .append(", alignment=").append(spring.getAlignment())
1249                 .append(" prefs=[").append(spring.getMinimumSize(axis))
1250                 .append(' ').append(spring.getPreferredSize(axis)).append(' ')
1251                 .append(spring.getMaximumSize(axis)).append(padding)
1252                 .append("]\n");
1253         if (spring instanceof Group) {
1254             List<Spring> springs = ((Group)spring).springs;
1255             indent += "  ";
1256             for (int counter = 0; counter < springs.size(); counter++) {
1257                 createSpringDescription(sb, springs.get(counter), indent,
1258                         axis);
1259             }
1260         }
1261     }
1262 
1263 
1264     /**
1265      * Spring consists of a range: min, pref and max, a value some where in
1266      * the middle of that, and a location. Spring caches the
1267      * min/max/pref.  If the min/pref/max has internally changes, or needs
1268      * to be updated you must invoke clear.
1269      */
1270     private abstract class Spring {
1271         private int size;
1272         private int min;
1273         private int max;
1274         private int pref;
1275         private Spring parent;
1276 
1277         private Alignment alignment;
1278 
1279         Spring() {
1280             min = pref = max = UNSET;
1281         }
1282 
1283         /**
1284          * Calculates and returns the minimum size.
1285          *
1286          * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
1287          * @return the minimum size
1288          */
1289         abstract int calculateMinimumSize(int axis);
1290 
1291         /**
1292          * Calculates and returns the preferred size.
1293          *
1294          * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
1295          * @return the preferred size
1296          */
1297         abstract int calculatePreferredSize(int axis);
1298 
1299         /**
1300          * Calculates and returns the minimum size.
1301          *
1302          * @param axis the axis of layout; one of HORIZONTAL or VERTICAL
1303          * @return the minimum size
1304          */
1305         abstract int calculateMaximumSize(int axis);
1306 
1307         /**
1308          * Sets the parent of this Spring.
1309          */
1310         void setParent(Spring parent) {
1311             this.parent = parent;
1312         }
1313 
1314         /**
1315          * Returns the parent of this spring.
1316          */
1317         Spring getParent() {
1318             return parent;
1319         }
1320 
1321         // This is here purely as a convenience for ParallelGroup to avoid
1322         // having to track alignment separately.
1323         void setAlignment(Alignment alignment) {
1324             this.alignment = alignment;
1325         }
1326 
1327         /**
1328          * Alignment for this Spring, this may be null.
1329          */
1330         Alignment getAlignment() {
1331             return alignment;
1332         }
1333 
1334         /**
1335          * Returns the minimum size.
1336          */
1337         final int getMinimumSize(int axis) {
1338             if (min == UNSET) {
1339                 min = constrain(calculateMinimumSize(axis));
1340             }
1341             return min;
1342         }
1343 
1344         /**
1345          * Returns the preferred size.
1346          */
1347         final int getPreferredSize(int axis) {
1348             if (pref == UNSET) {
1349                 pref = constrain(calculatePreferredSize(axis));
1350             }
1351             return pref;
1352         }
1353 
1354         /**
1355          * Returns the maximum size.
1356          */
1357         final int getMaximumSize(int axis) {
1358             if (max == UNSET) {
1359                 max = constrain(calculateMaximumSize(axis));
1360             }
1361             return max;
1362         }
1363 
1364         /**
1365          * Sets the value and location of the spring.  Subclasses
1366          * will want to invoke super, then do any additional sizing.
1367          *
1368          * @param axis HORIZONTAL or VERTICAL
1369          * @param origin of this Spring
1370          * @param size of the Spring.  If size is UNSET, this invokes
1371          *        clear.
1372          */
1373         void setSize(int axis, int origin, int size) {
1374             this.size = size;
1375             if (size == UNSET) {
1376                 unset();
1377             }
1378         }
1379 
1380         /**
1381          * Resets the cached min/max/pref.
1382          */
1383         void unset() {
1384             size = min = pref = max = UNSET;
1385         }
1386 
1387         /**
1388          * Returns the current size.
1389          */
1390         int getSize() {
1391             return size;
1392         }
1393 
1394         int constrain(int value) {
1395             return Math.min(value, INFINITE);
1396         }
1397 
1398         int getBaseline() {
1399             return -1;
1400         }
1401 
1402         BaselineResizeBehavior getBaselineResizeBehavior() {
1403             return BaselineResizeBehavior.OTHER;
1404         }
1405 
1406         final boolean isResizable(int axis) {
1407             int min = getMinimumSize(axis);
1408             int pref = getPreferredSize(axis);
1409             return (min != pref || pref != getMaximumSize(axis));
1410         }
1411 
1412         /**
1413          * Returns {@code true} if this spring will ALWAYS have a zero
1414          * size. This should NOT check the current size, rather it's
1415          * meant to quickly test if this Spring will always have a
1416          * zero size.
1417          *
1418          * @param treatAutopaddingAsZeroSized if {@code true}, auto padding
1419          *        springs should be treated as having a size of {@code 0}
1420          * @return {@code true} if this spring will have a zero size,
1421          *         {@code false} otherwise
1422          */
1423         abstract boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized);
1424     }
1425 
1426     /**
1427      * {@code Group} provides the basis for the two types of
1428      * operations supported by {@code GroupLayout}: laying out
1429      * components one after another ({@link SequentialGroup SequentialGroup})
1430      * or aligned ({@link ParallelGroup ParallelGroup}). {@code Group} and
1431      * its subclasses have no public constructor; to create one use
1432      * one of {@code createSequentialGroup} or
1433      * {@code createParallelGroup}. Additionally, taking a {@code Group}
1434      * created from one {@code GroupLayout} and using it with another
1435      * will produce undefined results.
1436      * <p>
1437      * Various methods in {@code Group} and its subclasses allow you
1438      * to explicitly specify the range. The arguments to these methods
1439      * can take two forms, either a value greater than or equal to 0,
1440      * or one of {@code DEFAULT_SIZE} or {@code PREFERRED_SIZE}. A
1441      * value greater than or equal to {@code 0} indicates a specific
1442      * size. {@code DEFAULT_SIZE} indicates the corresponding size
1443      * from the component should be used.  For example, if {@code
1444      * DEFAULT_SIZE} is passed as the minimum size argument, the
1445      * minimum size is obtained from invoking {@code getMinimumSize}
1446      * on the component. Likewise, {@code PREFERRED_SIZE} indicates
1447      * the value from {@code getPreferredSize} should be used.
1448      * The following example adds {@code myComponent} to {@code group}
1449      * with specific values for the range. That is, the minimum is
1450      * explicitly specified as 100, preferred as 200, and maximum as
1451      * 300.
1452      * <pre>
1453      *   group.addComponent(myComponent, 100, 200, 300);
1454      * </pre>
1455      * The following example adds {@code myComponent} to {@code group} using
1456      * a combination of the forms. The minimum size is forced to be the
1457      * same as the preferred size, the preferred size is determined by
1458      * using {@code myComponent.getPreferredSize} and the maximum is
1459      * determined by invoking {@code getMaximumSize} on the component.
1460      * <pre>
1461      *   group.addComponent(myComponent, GroupLayout.PREFERRED_SIZE,
1462      *             GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE);
1463      * </pre>
1464      * <p>
1465      * Unless otherwise specified all the methods of {@code Group} and
1466      * its subclasses that allow you to specify a range throw an
1467      * {@code IllegalArgumentException} if passed an invalid range. An
1468      * invalid range is one in which any of the values are &lt; 0 and
1469      * not one of {@code PREFERRED_SIZE} or {@code DEFAULT_SIZE}, or
1470      * the following is not met (for specific values): {@code min}
1471      * &lt;= {@code pref} &lt;= {@code max}.
1472      * <p>
1473      * Similarly any methods that take a {@code Component} throw a
1474      * {@code IllegalArgumentException} if passed {@code null} and any methods
1475      * that take a {@code Group} throw an {@code NullPointerException} if
1476      * passed {@code null}.
1477      *
1478      * @see #createSequentialGroup
1479      * @see #createParallelGroup
1480      * @since 1.6
1481      */
1482     public abstract class Group extends Spring {
1483         // private int origin;
1484         // private int size;
1485         List<Spring> springs;
1486 
1487         Group() {
1488             springs = new ArrayList<Spring>();
1489         }
1490 
1491         /**
1492          * Adds a {@code Group} to this {@code Group}.
1493          *
1494          * @param group the {@code Group} to add
1495          * @return this {@code Group}
1496          */
1497         public Group addGroup(Group group) {
1498             return addSpring(group);
1499         }
1500 
1501         /**
1502          * Adds a {@code Component} to this {@code Group}.
1503          *
1504          * @param component the {@code Component} to add
1505          * @return this {@code Group}
1506          */
1507         public Group addComponent(Component component) {
1508             return addComponent(component, DEFAULT_SIZE, DEFAULT_SIZE,
1509                     DEFAULT_SIZE);
1510         }
1511 
1512         /**
1513          * Adds a {@code Component} to this {@code Group}
1514          * with the specified size.
1515          *
1516          * @param component the {@code Component} to add
1517          * @param min the minimum size or one of {@code DEFAULT_SIZE} or
1518          *            {@code PREFERRED_SIZE}
1519          * @param pref the preferred size or one of {@code DEFAULT_SIZE} or
1520          *            {@code PREFERRED_SIZE}
1521          * @param max the maximum size or one of {@code DEFAULT_SIZE} or
1522          *            {@code PREFERRED_SIZE}
1523          * @return this {@code Group}
1524          */
1525         public Group addComponent(Component component, int min, int pref,
1526                 int max) {
1527             return addSpring(new ComponentSpring(component, min, pref, max));
1528         }
1529 
1530         /**
1531          * Adds a rigid gap to this {@code Group}.
1532          *
1533          * @param size the size of the gap
1534          * @return this {@code Group}
1535          * @throws IllegalArgumentException if {@code size} is less than
1536          *         {@code 0}
1537          */
1538         public Group addGap(int size) {
1539             return addGap(size, size, size);
1540         }
1541 
1542         /**
1543          * Adds a gap to this {@code Group} with the specified size.
1544          *
1545          * @param min the minimum size of the gap
1546          * @param pref the preferred size of the gap
1547          * @param max the maximum size of the gap
1548          * @throws IllegalArgumentException if any of the values are
1549          *         less than {@code 0}
1550          * @return this {@code Group}
1551          */
1552         public Group addGap(int min, int pref, int max) {
1553             return addSpring(new GapSpring(min, pref, max));
1554         }
1555 
1556         Spring getSpring(int index) {
1557             return springs.get(index);
1558         }
1559 
1560         int indexOf(Spring spring) {
1561             return springs.indexOf(spring);
1562         }
1563 
1564         /**
1565          * Adds the Spring to the list of {@code Spring}s and returns
1566          * the receiver.
1567          */
1568         Group addSpring(Spring spring) {
1569             springs.add(spring);
1570             spring.setParent(this);
1571             if (!(spring instanceof AutoPreferredGapSpring) ||
1572                     !((AutoPreferredGapSpring)spring).getUserCreated()) {
1573                 springsChanged = true;
1574             }
1575             return this;
1576         }
1577 
1578         //
1579         // Spring methods
1580         //
1581 
1582         void setSize(int axis, int origin, int size) {
1583             super.setSize(axis, origin, size);
1584             if (size == UNSET) {
1585                 for (int counter = springs.size() - 1; counter >= 0;
1586                 counter--) {
1587                     getSpring(counter).setSize(axis, origin, size);
1588                 }
1589             } else {
1590                 setValidSize(axis, origin, size);
1591             }
1592         }
1593 
1594         /**
1595          * This is invoked from {@code setSize} if passed a value
1596          * other than UNSET.
1597          */
1598         abstract void setValidSize(int axis, int origin, int size);
1599 
1600         int calculateMinimumSize(int axis) {
1601             return calculateSize(axis, MIN_SIZE);
1602         }
1603 
1604         int calculatePreferredSize(int axis) {
1605             return calculateSize(axis, PREF_SIZE);
1606         }
1607 
1608         int calculateMaximumSize(int axis) {
1609             return calculateSize(axis, MAX_SIZE);
1610         }
1611 
1612         /**
1613          * Calculates the specified size.  This is called from
1614          * one of the {@code getMinimumSize0},
1615          * {@code getPreferredSize0} or
1616          * {@code getMaximumSize0} methods.  This will invoke
1617          * to {@code operator} to combine the values.
1618          */
1619         int calculateSize(int axis, int type) {
1620             int count = springs.size();
1621             if (count == 0) {
1622                 return 0;
1623             }
1624             if (count == 1) {
1625                 return getSpringSize(getSpring(0), axis, type);
1626             }
1627             int size = constrain(operator(getSpringSize(getSpring(0), axis,
1628                     type), getSpringSize(getSpring(1), axis, type)));
1629             for (int counter = 2; counter < count; counter++) {
1630                 size = constrain(operator(size, getSpringSize(
1631                         getSpring(counter), axis, type)));
1632             }
1633             return size;
1634         }
1635 
1636         int getSpringSize(Spring spring, int axis, int type) {
1637             switch(type) {
1638                 case MIN_SIZE:
1639                     return spring.getMinimumSize(axis);
1640                 case PREF_SIZE:
1641                     return spring.getPreferredSize(axis);
1642                 case MAX_SIZE:
1643                     return spring.getMaximumSize(axis);
1644             }
1645             assert false;
1646             return 0;
1647         }
1648 
1649         /**
1650          * Used to compute how the two values representing two springs
1651          * will be combined.  For example, a group that layed things out
1652          * one after the next would return {@code a + b}.
1653          */
1654         abstract int operator(int a, int b);
1655 
1656         //
1657         // Padding
1658         //
1659 
1660         /**
1661          * Adjusts the autopadding springs in this group and its children.
1662          * If {@code insert} is true this will insert auto padding
1663          * springs, otherwise this will only adjust the springs that
1664          * comprise auto preferred padding springs.
1665          *
1666          * @param axis the axis of the springs; HORIZONTAL or VERTICAL
1667          * @param leadingPadding List of AutopaddingSprings that occur before
1668          *                       this Group
1669          * @param trailingPadding any trailing autopadding springs are added
1670          *                        to this on exit
1671          * @param leading List of ComponentSprings that occur before this Group
1672          * @param trailing any trailing ComponentSpring are added to this
1673          *                 List
1674          * @param insert Whether or not to insert AutopaddingSprings or just
1675          *               adjust any existing AutopaddingSprings.
1676          */
1677         abstract void insertAutopadding(int axis,
1678                 List<AutoPreferredGapSpring> leadingPadding,
1679                 List<AutoPreferredGapSpring> trailingPadding,
1680                 List<ComponentSpring> leading, List<ComponentSpring> trailing,
1681                 boolean insert);
1682 
1683         /**
1684          * Removes any AutopaddingSprings for this Group and its children.
1685          */
1686         void removeAutopadding() {
1687             unset();
1688             for (int counter = springs.size() - 1; counter >= 0; counter--) {
1689                 Spring spring = springs.get(counter);
1690                 if (spring instanceof AutoPreferredGapSpring) {
1691                     if (((AutoPreferredGapSpring)spring).getUserCreated()) {
1692                         ((AutoPreferredGapSpring)spring).reset();
1693                     } else {
1694                         springs.remove(counter);
1695                     }
1696                 } else if (spring instanceof Group) {
1697                     ((Group)spring).removeAutopadding();
1698                 }
1699             }
1700         }
1701 
1702         void unsetAutopadding() {
1703             // Clear cached pref/min/max.
1704             unset();
1705             for (int counter = springs.size() - 1; counter >= 0; counter--) {
1706                 Spring spring = springs.get(counter);
1707                 if (spring instanceof AutoPreferredGapSpring) {
1708                     spring.unset();
1709                 } else if (spring instanceof Group) {
1710                     ((Group)spring).unsetAutopadding();
1711                 }
1712             }
1713         }
1714 
1715         void calculateAutopadding(int axis) {
1716             for (int counter = springs.size() - 1; counter >= 0; counter--) {
1717                 Spring spring = springs.get(counter);
1718                 if (spring instanceof AutoPreferredGapSpring) {
1719                     // Force size to be reset.
1720                     spring.unset();
1721                     ((AutoPreferredGapSpring)spring).calculatePadding(axis);
1722                 } else if (spring instanceof Group) {
1723                     ((Group)spring).calculateAutopadding(axis);
1724                 }
1725             }
1726             // Clear cached pref/min/max.
1727             unset();
1728         }
1729 
1730         @Override
1731         boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
1732             for (int i = springs.size() - 1; i >= 0; i--) {
1733                 Spring spring = springs.get(i);
1734                 if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {
1735                     return false;
1736                 }
1737             }
1738             return true;
1739         }
1740     }
1741 
1742 
1743     /**
1744      * A {@code Group} that positions and sizes its elements
1745      * sequentially, one after another.  This class has no public
1746      * constructor, use the {@code createSequentialGroup} method
1747      * to create one.
1748      * <p>
1749      * In order to align a {@code SequentialGroup} along the baseline
1750      * of a baseline aligned {@code ParallelGroup} you need to specify
1751      * which of the elements of the {@code SequentialGroup} is used to
1752      * determine the baseline.  The element used to calculate the
1753      * baseline is specified using one of the {@code add} methods that
1754      * take a {@code boolean}. The last element added with a value of
1755      * {@code true} for {@code useAsBaseline} is used to calculate the
1756      * baseline.
1757      *
1758      * @see #createSequentialGroup
1759      * @since 1.6
1760      */
1761     public class SequentialGroup extends Group {
1762         private Spring baselineSpring;
1763 
1764         SequentialGroup() {
1765         }
1766 
1767         /**
1768          * {@inheritDoc}
1769          */
1770         public SequentialGroup addGroup(Group group) {
1771             return (SequentialGroup)super.addGroup(group);
1772         }
1773 
1774         /**
1775          * Adds a {@code Group} to this {@code Group}.
1776          *
1777          * @param group the {@code Group} to add
1778          * @param useAsBaseline whether the specified {@code Group} should
1779          *        be used to calculate the baseline for this {@code Group}
1780          * @return this {@code Group}
1781          */
1782         public SequentialGroup addGroup(boolean useAsBaseline, Group group) {
1783             super.addGroup(group);
1784             if (useAsBaseline) {
1785                 baselineSpring = group;
1786             }
1787             return this;
1788         }
1789 
1790         /**
1791          * {@inheritDoc}
1792          */
1793         public SequentialGroup addComponent(Component component) {
1794             return (SequentialGroup)super.addComponent(component);
1795         }
1796 
1797         /**
1798          * Adds a {@code Component} to this {@code Group}.
1799          *
1800          * @param useAsBaseline whether the specified {@code Component} should
1801          *        be used to calculate the baseline for this {@code Group}
1802          * @param component the {@code Component} to add
1803          * @return this {@code Group}
1804          */
1805         public SequentialGroup addComponent(boolean useAsBaseline,
1806                 Component component) {
1807             super.addComponent(component);
1808             if (useAsBaseline) {
1809                 baselineSpring = springs.get(springs.size() - 1);
1810             }
1811             return this;
1812         }
1813 
1814         /**
1815          * {@inheritDoc}
1816          */
1817         public SequentialGroup addComponent(Component component, int min,
1818                 int pref, int max) {
1819             return (SequentialGroup)super.addComponent(
1820                     component, min, pref, max);
1821         }
1822 
1823         /**
1824          * Adds a {@code Component} to this {@code Group}
1825          * with the specified size.
1826          *
1827          * @param useAsBaseline whether the specified {@code Component} should
1828          *        be used to calculate the baseline for this {@code Group}
1829          * @param component the {@code Component} to add
1830          * @param min the minimum size or one of {@code DEFAULT_SIZE} or
1831          *            {@code PREFERRED_SIZE}
1832          * @param pref the preferred size or one of {@code DEFAULT_SIZE} or
1833          *            {@code PREFERRED_SIZE}
1834          * @param max the maximum size or one of {@code DEFAULT_SIZE} or
1835          *            {@code PREFERRED_SIZE}
1836          * @return this {@code Group}
1837          */
1838         public SequentialGroup addComponent(boolean useAsBaseline,
1839                 Component component, int min, int pref, int max) {
1840             super.addComponent(component, min, pref, max);
1841             if (useAsBaseline) {
1842                 baselineSpring = springs.get(springs.size() - 1);
1843             }
1844             return this;
1845         }
1846 
1847         /**
1848          * {@inheritDoc}
1849          */
1850         public SequentialGroup addGap(int size) {
1851             return (SequentialGroup)super.addGap(size);
1852         }
1853 
1854         /**
1855          * {@inheritDoc}
1856          */
1857         public SequentialGroup addGap(int min, int pref, int max) {
1858             return (SequentialGroup)super.addGap(min, pref, max);
1859         }
1860 
1861         /**
1862          * Adds an element representing the preferred gap between two
1863          * components. The element created to represent the gap is not
1864          * resizable.
1865          *
1866          * @param comp1 the first component
1867          * @param comp2 the second component
1868          * @param type the type of gap; one of the constants defined by
1869          *        {@code LayoutStyle}
1870          * @return this {@code SequentialGroup}
1871          * @throws IllegalArgumentException if {@code type}, {@code comp1} or
1872          *         {@code comp2} is {@code null}
1873          * @see LayoutStyle
1874          */
1875         public SequentialGroup addPreferredGap(JComponent comp1,
1876                 JComponent comp2, ComponentPlacement type) {
1877             return addPreferredGap(comp1, comp2, type, DEFAULT_SIZE,
1878                     PREFERRED_SIZE);
1879         }
1880 
1881         /**
1882          * Adds an element representing the preferred gap between two
1883          * components.
1884          *
1885          * @param comp1 the first component
1886          * @param comp2 the second component
1887          * @param type the type of gap
1888          * @param pref the preferred size of the grap; one of
1889          *        {@code DEFAULT_SIZE} or a value &gt;= 0
1890          * @param max the maximum size of the gap; one of
1891          *        {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}
1892          *        or a value &gt;= 0
1893          * @return this {@code SequentialGroup}
1894          * @throws IllegalArgumentException if {@code type}, {@code comp1} or
1895          *         {@code comp2} is {@code null}
1896          * @see LayoutStyle
1897          */
1898         public SequentialGroup addPreferredGap(JComponent comp1,
1899                 JComponent comp2, ComponentPlacement type, int pref,
1900                 int max) {
1901             if (type == null) {
1902                 throw new IllegalArgumentException("Type must be non-null");
1903             }
1904             if (comp1 == null || comp2 == null) {
1905                 throw new IllegalArgumentException(
1906                         "Components must be non-null");
1907             }
1908             checkPreferredGapValues(pref, max);
1909             return (SequentialGroup)addSpring(new PreferredGapSpring(
1910                     comp1, comp2, type, pref, max));
1911         }
1912 
1913         /**
1914          * Adds an element representing the preferred gap between the
1915          * nearest components.  During layout, neighboring
1916          * components are found, and the size of the added gap is set
1917          * based on the preferred gap between the components.  If no
1918          * neighboring components are found the gap has a size of {@code 0}.
1919          * <p>
1920          * The element created to represent the gap is not
1921          * resizable.
1922          *
1923          * @param type the type of gap; one of
1924          *        {@code LayoutStyle.ComponentPlacement.RELATED} or
1925          *        {@code LayoutStyle.ComponentPlacement.UNRELATED}
1926          * @return this {@code SequentialGroup}
1927          * @see LayoutStyle
1928          * @throws IllegalArgumentException if {@code type} is not one of
1929          *         {@code LayoutStyle.ComponentPlacement.RELATED} or
1930          *         {@code LayoutStyle.ComponentPlacement.UNRELATED}
1931          */
1932         public SequentialGroup addPreferredGap(ComponentPlacement type) {
1933             return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);
1934         }
1935 
1936         /**
1937          * Adds an element representing the preferred gap between the
1938          * nearest components.  During layout, neighboring
1939          * components are found, and the minimum of this
1940          * gap is set based on the size of the preferred gap between the
1941          * neighboring components.  If no neighboring components are found the
1942          * minimum size is set to 0.
1943          *
1944          * @param type the type of gap; one of
1945          *        {@code LayoutStyle.ComponentPlacement.RELATED} or
1946          *        {@code LayoutStyle.ComponentPlacement.UNRELATED}
1947          * @param pref the preferred size of the grap; one of
1948          *        {@code DEFAULT_SIZE} or a value &gt;= 0
1949          * @param max the maximum size of the gap; one of
1950          *        {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE}
1951          *        or a value &gt;= 0
1952          * @return this {@code SequentialGroup}
1953          * @throws IllegalArgumentException if {@code type} is not one of
1954          *         {@code LayoutStyle.ComponentPlacement.RELATED} or
1955          *         {@code LayoutStyle.ComponentPlacement.UNRELATED}
1956          * @see LayoutStyle
1957          */
1958         public SequentialGroup addPreferredGap(ComponentPlacement type,
1959                 int pref, int max) {
1960             if (type != ComponentPlacement.RELATED &&
1961                     type != ComponentPlacement.UNRELATED) {
1962                 throw new IllegalArgumentException(
1963                         "Type must be one of " +
1964                         "LayoutStyle.ComponentPlacement.RELATED or " +
1965                         "LayoutStyle.ComponentPlacement.UNRELATED");
1966             }
1967             checkPreferredGapValues(pref, max);
1968             hasPreferredPaddingSprings = true;
1969             return (SequentialGroup)addSpring(new AutoPreferredGapSpring(
1970                     type, pref, max));
1971         }
1972 
1973         /**
1974          * Adds an element representing the preferred gap between an edge
1975          * the container and components that touch the border of the
1976          * container. This has no effect if the added gap does not
1977          * touch an edge of the parent container.
1978          * <p>
1979          * The element created to represent the gap is not
1980          * resizable.
1981          *
1982          * @return this {@code SequentialGroup}
1983          */
1984         public SequentialGroup addContainerGap() {
1985             return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);
1986         }
1987 
1988         /**
1989          * Adds an element representing the preferred gap between one
1990          * edge of the container and the next or previous {@code
1991          * Component} with the specified size. This has no
1992          * effect if the next or previous element is not a {@code
1993          * Component} and does not touch one edge of the parent
1994          * container.
1995          *
1996          * @param pref the preferred size; one of {@code DEFAULT_SIZE} or a
1997          *              value &gt;= 0
1998          * @param max the maximum size; one of {@code DEFAULT_SIZE},
1999          *        {@code PREFERRED_SIZE} or a value &gt;= 0
2000          * @return this {@code SequentialGroup}
2001          */
2002         public SequentialGroup addContainerGap(int pref, int max) {
2003             if ((pref < 0 && pref != DEFAULT_SIZE) ||
2004                     (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
2005                     (pref >= 0 && max >= 0 && pref > max)) {
2006                 throw new IllegalArgumentException(
2007                         "Pref and max must be either DEFAULT_VALUE " +
2008                         "or >= 0 and pref <= max");
2009             }
2010             hasPreferredPaddingSprings = true;
2011             return (SequentialGroup)addSpring(
2012                     new ContainerAutoPreferredGapSpring(pref, max));
2013         }
2014 
2015         int operator(int a, int b) {
2016             return constrain(a) + constrain(b);
2017         }
2018 
2019         void setValidSize(int axis, int origin, int size) {
2020             int pref = getPreferredSize(axis);
2021             if (size == pref) {
2022                 // Layout at preferred size
2023                 for (Spring spring : springs) {
2024                     int springPref = spring.getPreferredSize(axis);
2025                     spring.setSize(axis, origin, springPref);
2026                     origin += springPref;
2027                 }
2028             } else if (springs.size() == 1) {
2029                 Spring spring = getSpring(0);
2030                 spring.setSize(axis, origin, Math.min(
2031                         Math.max(size, spring.getMinimumSize(axis)),
2032                         spring.getMaximumSize(axis)));
2033             } else if (springs.size() > 1) {
2034                 // Adjust between min/pref
2035                 setValidSizeNotPreferred(axis, origin, size);
2036             }
2037         }
2038 
2039         private void setValidSizeNotPreferred(int axis, int origin, int size) {
2040             int delta = size - getPreferredSize(axis);
2041             assert delta != 0;
2042             boolean useMin = (delta < 0);
2043             int springCount = springs.size();
2044             if (useMin) {
2045                 delta *= -1;
2046             }
2047 
2048             // The following algorithm if used for resizing springs:
2049             // 1. Calculate the resizability of each spring (pref - min or
2050             //    max - pref) into a list.
2051             // 2. Sort the list in ascending order
2052             // 3. Iterate through each of the resizable Springs, attempting
2053             //    to give them (pref - size) / resizeCount
2054             // 4. For any Springs that can not accommodate that much space
2055             //    add the remainder back to the amount to distribute and
2056             //    recalculate how must space the remaining springs will get.
2057             // 5. Set the size of the springs.
2058 
2059             // First pass, sort the resizable springs into the List resizable
2060             List<SpringDelta> resizable = buildResizableList(axis, useMin);
2061             int resizableCount = resizable.size();
2062 
2063             if (resizableCount > 0) {
2064                 // How much we would like to give each Spring.
2065                 int sDelta = delta / resizableCount;
2066                 // Remaining space.
2067                 int slop = delta - sDelta * resizableCount;
2068                 int[] sizes = new int[springCount];
2069                 int sign = useMin ? -1 : 1;
2070                 // Second pass, accumulate the resulting deltas (relative to
2071                 // preferred) into sizes.
2072                 for (int counter = 0; counter < resizableCount; counter++) {
2073                     SpringDelta springDelta = resizable.get(counter);
2074                     if ((counter + 1) == resizableCount) {
2075                         sDelta += slop;
2076                     }
2077                     springDelta.delta = Math.min(sDelta, springDelta.delta);
2078                     delta -= springDelta.delta;
2079                     if (springDelta.delta != sDelta && counter + 1 <
2080                             resizableCount) {
2081                         // Spring didn't take all the space, reset how much
2082                         // each spring will get.
2083                         sDelta = delta / (resizableCount - counter - 1);
2084                         slop = delta - sDelta * (resizableCount - counter - 1);
2085                     }
2086                     sizes[springDelta.index] = sign * springDelta.delta;
2087                 }
2088 
2089                 // And finally set the size of each spring
2090                 for (int counter = 0; counter < springCount; counter++) {
2091                     Spring spring = getSpring(counter);
2092                     int sSize = spring.getPreferredSize(axis) + sizes[counter];
2093                     spring.setSize(axis, origin, sSize);
2094                     origin += sSize;
2095                 }
2096             } else {
2097                 // Nothing resizable, use the min or max of each of the
2098                 // springs.
2099                 for (int counter = 0; counter < springCount; counter++) {
2100                     Spring spring = getSpring(counter);
2101                     int sSize;
2102                     if (useMin) {
2103                         sSize = spring.getMinimumSize(axis);
2104                     } else {
2105                         sSize = spring.getMaximumSize(axis);
2106                     }
2107                     spring.setSize(axis, origin, sSize);
2108                     origin += sSize;
2109                 }
2110             }
2111         }
2112 
2113         /**
2114          * Returns the sorted list of SpringDelta's for the current set of
2115          * Springs. The list is ordered based on the amount of flexibility of
2116          * the springs.
2117          */
2118         private List<SpringDelta> buildResizableList(int axis,
2119                 boolean useMin) {
2120             // First pass, figure out what is resizable
2121             int size = springs.size();
2122             List<SpringDelta> sorted = new ArrayList<SpringDelta>(size);
2123             for (int counter = 0; counter < size; counter++) {
2124                 Spring spring = getSpring(counter);
2125                 int sDelta;
2126                 if (useMin) {
2127                     sDelta = spring.getPreferredSize(axis) -
2128                             spring.getMinimumSize(axis);
2129                 } else {
2130                     sDelta = spring.getMaximumSize(axis) -
2131                             spring.getPreferredSize(axis);
2132                 }
2133                 if (sDelta > 0) {
2134                     sorted.add(new SpringDelta(counter, sDelta));
2135                 }
2136             }
2137             Collections.sort(sorted);
2138             return sorted;
2139         }
2140 
2141         private int indexOfNextNonZeroSpring(
2142                 int index, boolean treatAutopaddingAsZeroSized) {
2143             while (index < springs.size()) {
2144                 Spring spring = springs.get(index);
2145                 if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {
2146                     return index;
2147                 }
2148                 index++;
2149             }
2150             return index;
2151         }
2152 
2153         @Override
2154         void insertAutopadding(int axis,
2155                 List<AutoPreferredGapSpring> leadingPadding,
2156                 List<AutoPreferredGapSpring> trailingPadding,
2157                 List<ComponentSpring> leading, List<ComponentSpring> trailing,
2158                 boolean insert) {
2159             List<AutoPreferredGapSpring> newLeadingPadding =
2160                     new ArrayList<AutoPreferredGapSpring>(leadingPadding);
2161             List<AutoPreferredGapSpring> newTrailingPadding =
2162                     new ArrayList<AutoPreferredGapSpring>(1);
2163             List<ComponentSpring> newLeading =
2164                     new ArrayList<ComponentSpring>(leading);
2165             List<ComponentSpring> newTrailing = null;
2166             int counter = 0;
2167             // Warning, this must use springs.size, as it may change during the
2168             // loop.
2169             while (counter < springs.size()) {
2170                 Spring spring = getSpring(counter);
2171                 if (spring instanceof AutoPreferredGapSpring) {
2172                     if (newLeadingPadding.size() == 0) {
2173                         // Autopadding spring. Set the sources of the
2174                         // autopadding spring based on newLeading.
2175                         AutoPreferredGapSpring padding =
2176                             (AutoPreferredGapSpring)spring;
2177                         padding.setSources(newLeading);
2178                         newLeading.clear();
2179                         counter = indexOfNextNonZeroSpring(counter + 1, true);
2180                         if (counter == springs.size()) {
2181                             // Last spring in the list, add it to
2182                             // trailingPadding.
2183                             if (!(padding instanceof
2184                                   ContainerAutoPreferredGapSpring)) {
2185                                 trailingPadding.add(padding);
2186                             }
2187                         } else {
2188                             newLeadingPadding.clear();
2189                             newLeadingPadding.add(padding);
2190                         }
2191                     } else {
2192                         counter = indexOfNextNonZeroSpring(counter + 1, true);
2193                     }
2194                 } else {
2195                     // Not a padding spring
2196                     if (newLeading.size() > 0 && newLeadingPadding.isEmpty() && insert) {
2197                         // There's leading ComponentSprings, create an
2198                         // autopadding spring.
2199                         AutoPreferredGapSpring padding =
2200                                 new AutoPreferredGapSpring();
2201                         // Force the newly created spring to be considered
2202                         // by NOT incrementing counter
2203                         springs.add(counter, padding);
2204                         continue;
2205                     }
2206                     if (spring instanceof ComponentSpring) {
2207                         // Spring is a Component, make it the target of any
2208                         // leading AutopaddingSpring.
2209                         ComponentSpring cSpring = (ComponentSpring)spring;
2210                         if (!cSpring.isVisible()) {
2211                             counter++;
2212                             continue;
2213                         }
2214                         for (AutoPreferredGapSpring gapSpring : newLeadingPadding) {
2215                             gapSpring.addTarget(cSpring, axis);
2216                         }
2217                         newLeading.clear();
2218                         newLeadingPadding.clear();
2219                         counter = indexOfNextNonZeroSpring(counter + 1, false);
2220                         if (counter == springs.size()) {
2221                             // Last Spring, add it to trailing
2222                             trailing.add(cSpring);
2223                         } else {
2224                             // Not that last Spring, add it to leading
2225                             newLeading.add(cSpring);
2226                         }
2227                     } else if (spring instanceof Group) {
2228                         // Forward call to child Group
2229                         if (newTrailing == null) {
2230                             newTrailing = new ArrayList<ComponentSpring>(1);
2231                         } else {
2232                             newTrailing.clear();
2233                         }
2234                         newTrailingPadding.clear();
2235                         ((Group)spring).insertAutopadding(axis,
2236                                 newLeadingPadding, newTrailingPadding,
2237                                 newLeading, newTrailing, insert);
2238                         newLeading.clear();
2239                         newLeadingPadding.clear();
2240                         counter = indexOfNextNonZeroSpring(
2241                                     counter + 1, (newTrailing.size() == 0));
2242                         if (counter == springs.size()) {
2243                             trailing.addAll(newTrailing);
2244                             trailingPadding.addAll(newTrailingPadding);
2245                         } else {
2246                             newLeading.addAll(newTrailing);
2247                             newLeadingPadding.addAll(newTrailingPadding);
2248                         }
2249                     } else {
2250                         // Gap
2251                         newLeadingPadding.clear();
2252                         newLeading.clear();
2253                         counter++;
2254                     }
2255                 }
2256             }
2257         }
2258 
2259         int getBaseline() {
2260             if (baselineSpring != null) {
2261                 int baseline = baselineSpring.getBaseline();
2262                 if (baseline >= 0) {
2263                     int size = 0;
2264                     for (Spring spring : springs) {
2265                         if (spring == baselineSpring) {
2266                             return size + baseline;
2267                         } else {
2268                             size += spring.getPreferredSize(VERTICAL);
2269                         }
2270                     }
2271                 }
2272             }
2273             return -1;
2274         }
2275 
2276         BaselineResizeBehavior getBaselineResizeBehavior() {
2277             if (isResizable(VERTICAL)) {
2278                 if (!baselineSpring.isResizable(VERTICAL)) {
2279                     // Spring to use for baseline isn't resizable. In this case
2280                     // baseline resize behavior can be determined based on how
2281                     // preceding springs resize.
2282                     boolean leadingResizable = false;
2283                     for (Spring spring : springs) {
2284                         if (spring == baselineSpring) {
2285                             break;
2286                         } else if (spring.isResizable(VERTICAL)) {
2287                             leadingResizable = true;
2288                             break;
2289                         }
2290                     }
2291                     boolean trailingResizable = false;
2292                     for (int i = springs.size() - 1; i >= 0; i--) {
2293                         Spring spring = springs.get(i);
2294                         if (spring == baselineSpring) {
2295                             break;
2296                         }
2297                         if (spring.isResizable(VERTICAL)) {
2298                             trailingResizable = true;
2299                             break;
2300                         }
2301                     }
2302                     if (leadingResizable && !trailingResizable) {
2303                         return BaselineResizeBehavior.CONSTANT_DESCENT;
2304                     } else if (!leadingResizable && trailingResizable) {
2305                         return BaselineResizeBehavior.CONSTANT_ASCENT;
2306                     }
2307                     // If we get here, both leading and trailing springs are
2308                     // resizable. Fall through to OTHER.
2309                 } else {
2310                     BaselineResizeBehavior brb = baselineSpring.getBaselineResizeBehavior();
2311                     if (brb == BaselineResizeBehavior.CONSTANT_ASCENT) {
2312                         for (Spring spring : springs) {
2313                             if (spring == baselineSpring) {
2314                                 return BaselineResizeBehavior.CONSTANT_ASCENT;
2315                             }
2316                             if (spring.isResizable(VERTICAL)) {
2317                                 return BaselineResizeBehavior.OTHER;
2318                             }
2319                         }
2320                     } else if (brb == BaselineResizeBehavior.CONSTANT_DESCENT) {
2321                         for (int i = springs.size() - 1; i >= 0; i--) {
2322                             Spring spring = springs.get(i);
2323                             if (spring == baselineSpring) {
2324                                 return BaselineResizeBehavior.CONSTANT_DESCENT;
2325                             }
2326                             if (spring.isResizable(VERTICAL)) {
2327                                 return BaselineResizeBehavior.OTHER;
2328                             }
2329                         }
2330                     }
2331                 }
2332                 return BaselineResizeBehavior.OTHER;
2333             }
2334             // Not resizable, treat as constant_ascent
2335             return BaselineResizeBehavior.CONSTANT_ASCENT;
2336         }
2337 
2338         private void checkPreferredGapValues(int pref, int max) {
2339             if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) ||
2340                     (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
2341                     (pref >= 0 && max >= 0 && pref > max)) {
2342                 throw new IllegalArgumentException(
2343                         "Pref and max must be either DEFAULT_SIZE, " +
2344                         "PREFERRED_SIZE, or >= 0 and pref <= max");
2345             }
2346         }
2347     }
2348 
2349 
2350     /**
2351      * Used by SequentialGroup in calculating resizability of springs.
2352      */
2353     private static final class SpringDelta implements Comparable<SpringDelta> {
2354         // Original index.
2355         public final int index;
2356         // Delta, one of pref - min or max - pref.
2357         public int delta;
2358 
2359         public SpringDelta(int index, int delta) {
2360             this.index = index;
2361             this.delta = delta;
2362         }
2363 
2364         public int compareTo(SpringDelta o) {
2365             return delta - o.delta;
2366         }
2367 
2368         public String toString() {
2369             return super.toString() + "[index=" + index + ", delta=" +
2370                     delta + "]";
2371         }
2372     }
2373 
2374 
2375     /**
2376      * A {@code Group} that aligns and sizes it's children.
2377      * {@code ParallelGroup} aligns it's children in
2378      * four possible ways: along the baseline, centered, anchored to the
2379      * leading edge, or anchored to the trailing edge.
2380      * <h3>Baseline</h3>
2381      * A {@code ParallelGroup} that aligns it's children along the
2382      * baseline must first decide where the baseline is
2383      * anchored. The baseline can either be anchored to the top, or
2384      * anchored to the bottom of the group. That is, the distance between the
2385      * baseline and the beginning of the group can be a constant
2386      * distance, or the distance between the end of the group and the
2387      * baseline can be a constant distance. The possible choices
2388      * correspond to the {@code BaselineResizeBehavior} constants
2389      * {@link
2390      * java.awt.Component.BaselineResizeBehavior#CONSTANT_ASCENT CONSTANT_ASCENT} and
2391      * {@link
2392      * java.awt.Component.BaselineResizeBehavior#CONSTANT_DESCENT CONSTANT_DESCENT}.
2393      * <p>
2394      * The baseline anchor may be explicitly specified by the
2395      * {@code createBaselineGroup} method, or determined based on the elements.
2396      * If not explicitly specified, the baseline will be anchored to
2397      * the bottom if all the elements with a baseline, and that are
2398      * aligned to the baseline, have a baseline resize behavior of
2399      * {@code CONSTANT_DESCENT}; otherwise the baseline is anchored to the top
2400      * of the group.
2401      * <p>
2402      * Elements aligned to the baseline are resizable if they have
2403      * a baseline resize behavior of {@code CONSTANT_ASCENT} or
2404      * {@code CONSTANT_DESCENT}. Elements with a baseline resize
2405      * behavior of {@code OTHER} or {@code CENTER_OFFSET} are not resizable.
2406      * <p>
2407      * The baseline is calculated based on the preferred height of each
2408      * of the elements that have a baseline. The baseline is
2409      * calculated using the following algorithm:
2410      * {@code max(maxNonBaselineHeight, maxAscent + maxDescent)}, where the
2411      * {@code maxNonBaselineHeight} is the maximum height of all elements
2412      * that do not have a baseline, or are not aligned along the baseline.
2413      * {@code maxAscent} is the maximum ascent (baseline) of all elements that
2414      * have a baseline and are aligned along the baseline.
2415      * {@code maxDescent} is the maximum descent (preferred height - baseline)
2416      * of all elements that have a baseline and are aligned along the baseline.
2417      * <p>
2418      * A {@code ParallelGroup} that aligns it's elements along the baseline
2419      * is only useful along the vertical axis. If you create a
2420      * baseline group and use it along the horizontal axis an
2421      * {@code IllegalStateException} is thrown when you ask
2422      * {@code GroupLayout} for the minimum, preferred or maximum size or
2423      * attempt to layout the components.
2424      * <p>
2425      * Elements that are not aligned to the baseline and smaller than the size
2426      * of the {@code ParallelGroup} are positioned in one of three
2427      * ways: centered, anchored to the leading edge, or anchored to the
2428      * trailing edge.
2429      *
2430      * <h3>Non-baseline {@code ParallelGroup}</h3>
2431      * {@code ParallelGroup}s created with an alignment other than
2432      * {@code BASELINE} align elements that are smaller than the size
2433      * of the group in one of three ways: centered, anchored to the
2434      * leading edge, or anchored to the trailing edge.
2435      * <p>
2436      * The leading edge is based on the axis and {@code
2437      * ComponentOrientation}.  For the vertical axis the top edge is
2438      * always the leading edge, and the bottom edge is always the
2439      * trailing edge. When the {@code ComponentOrientation} is {@code
2440      * LEFT_TO_RIGHT}, the leading edge is the left edge and the
2441      * trailing edge the right edge. A {@code ComponentOrientation} of
2442      * {@code RIGHT_TO_LEFT} flips the left and right edges. Child
2443      * elements are aligned based on the specified alignment the
2444      * element was added with. If you do not specify an alignment, the
2445      * alignment specified for the {@code ParallelGroup} is used.
2446      * <p>
2447      * To align elements along the baseline you {@code createBaselineGroup},
2448      * or {@code createParallelGroup} with an alignment of {@code BASELINE}.
2449      * If the group was not created with a baseline alignment, and you attempt
2450      * to add an element specifying a baseline alignment, an
2451      * {@code IllegalArgumentException} is thrown.
2452      *
2453      * @see #createParallelGroup()
2454      * @see #createBaselineGroup(boolean,boolean)
2455      * @since 1.6
2456      */
2457     public class ParallelGroup extends Group {
2458         // How children are layed out.
2459         private final Alignment childAlignment;
2460         // Whether or not we're resizable.
2461         private final boolean resizable;
2462 
2463         ParallelGroup(Alignment childAlignment, boolean resizable) {
2464             this.childAlignment = childAlignment;
2465             this.resizable = resizable;
2466         }
2467 
2468         /**
2469          * {@inheritDoc}
2470          */
2471         public ParallelGroup addGroup(Group group) {
2472             return (ParallelGroup)super.addGroup(group);
2473         }
2474 
2475         /**
2476          * {@inheritDoc}
2477          */
2478         public ParallelGroup addComponent(Component component) {
2479             return (ParallelGroup)super.addComponent(component);
2480         }
2481 
2482         /**
2483          * {@inheritDoc}
2484          */
2485         public ParallelGroup addComponent(Component component, int min, int pref,
2486                 int max) {
2487             return (ParallelGroup)super.addComponent(component, min, pref, max);
2488         }
2489 
2490         /**
2491          * {@inheritDoc}
2492          */
2493         public ParallelGroup addGap(int pref) {
2494             return (ParallelGroup)super.addGap(pref);
2495         }
2496 
2497         /**
2498          * {@inheritDoc}
2499          */
2500         public ParallelGroup addGap(int min, int pref, int max) {
2501             return (ParallelGroup)super.addGap(min, pref, max);
2502         }
2503 
2504         /**
2505          * Adds a {@code Group} to this {@code ParallelGroup} with the
2506          * specified alignment. If the child is smaller than the
2507          * {@code Group} it is aligned based on the specified
2508          * alignment.
2509          *
2510          * @param alignment the alignment
2511          * @param group the {@code Group} to add
2512          * @return this {@code ParallelGroup}
2513          * @throws IllegalArgumentException if {@code alignment} is
2514          *         {@code null}
2515          */
2516         public ParallelGroup addGroup(Alignment alignment, Group group) {
2517             checkChildAlignment(alignment);
2518             group.setAlignment(alignment);
2519             return (ParallelGroup)addSpring(group);
2520         }
2521 
2522         /**
2523          * Adds a {@code Component} to this {@code ParallelGroup} with
2524          * the specified alignment.
2525          *
2526          * @param alignment the alignment
2527          * @param component the {@code Component} to add
2528          * @return this {@code Group}
2529          * @throws IllegalArgumentException if {@code alignment} is
2530          *         {@code null}
2531          */
2532         public ParallelGroup addComponent(Component component,
2533                 Alignment alignment) {
2534             return addComponent(component, alignment, DEFAULT_SIZE, DEFAULT_SIZE,
2535                     DEFAULT_SIZE);
2536         }
2537 
2538         /**
2539          * Adds a {@code Component} to this {@code ParallelGroup} with the
2540          * specified alignment and size.
2541          *
2542          * @param alignment the alignment
2543          * @param component the {@code Component} to add
2544          * @param min the minimum size
2545          * @param pref the preferred size
2546          * @param max the maximum size
2547          * @throws IllegalArgumentException if {@code alignment} is
2548          *         {@code null}
2549          * @return this {@code Group}
2550          */
2551         public ParallelGroup addComponent(Component component,
2552                 Alignment alignment, int min, int pref, int max) {
2553             checkChildAlignment(alignment);
2554             ComponentSpring spring = new ComponentSpring(component,
2555                     min, pref, max);
2556             spring.setAlignment(alignment);
2557             return (ParallelGroup)addSpring(spring);
2558         }
2559 
2560         boolean isResizable() {
2561             return resizable;
2562         }
2563 
2564         int operator(int a, int b) {
2565             return Math.max(a, b);
2566         }
2567 
2568         int calculateMinimumSize(int axis) {
2569             if (!isResizable()) {
2570                 return getPreferredSize(axis);
2571             }
2572             return super.calculateMinimumSize(axis);
2573         }
2574 
2575         int calculateMaximumSize(int axis) {
2576             if (!isResizable()) {
2577                 return getPreferredSize(axis);
2578             }
2579             return super.calculateMaximumSize(axis);
2580         }
2581 
2582         void setValidSize(int axis, int origin, int size) {
2583             for (Spring spring : springs) {
2584                 setChildSize(spring, axis, origin, size);
2585             }
2586         }
2587 
2588         void setChildSize(Spring spring, int axis, int origin, int size) {
2589             Alignment alignment = spring.getAlignment();
2590             int springSize = Math.min(
2591                     Math.max(spring.getMinimumSize(axis), size),
2592                     spring.getMaximumSize(axis));
2593             if (alignment == null) {
2594                 alignment = childAlignment;
2595             }
2596             switch (alignment) {
2597                 case TRAILING:
2598                     spring.setSize(axis, origin + size - springSize,
2599                             springSize);
2600                     break;
2601                 case CENTER:
2602                     spring.setSize(axis, origin +
2603                             (size - springSize) / 2,springSize);
2604                     break;
2605                 default: // LEADING, or BASELINE
2606                     spring.setSize(axis, origin, springSize);
2607                     break;
2608             }
2609         }
2610 
2611         @Override
2612         void insertAutopadding(int axis,
2613                 List<AutoPreferredGapSpring> leadingPadding,
2614                 List<AutoPreferredGapSpring> trailingPadding,
2615                 List<ComponentSpring> leading, List<ComponentSpring> trailing,
2616                 boolean insert) {
2617             for (Spring spring : springs) {
2618                 if (spring instanceof ComponentSpring) {
2619                     if (((ComponentSpring)spring).isVisible()) {
2620                         for (AutoPreferredGapSpring gapSpring :
2621                                  leadingPadding) {
2622                             gapSpring.addTarget((ComponentSpring)spring, axis);
2623                         }
2624                         trailing.add((ComponentSpring)spring);
2625                     }
2626                 } else if (spring instanceof Group) {
2627                     ((Group)spring).insertAutopadding(axis, leadingPadding,
2628                             trailingPadding, leading, trailing, insert);
2629                 } else if (spring instanceof AutoPreferredGapSpring) {
2630                     ((AutoPreferredGapSpring)spring).setSources(leading);
2631                     trailingPadding.add((AutoPreferredGapSpring)spring);
2632                 }
2633             }
2634         }
2635 
2636         private void checkChildAlignment(Alignment alignment) {
2637             checkChildAlignment(alignment, (this instanceof BaselineGroup));
2638         }
2639 
2640         private void checkChildAlignment(Alignment alignment,
2641                 boolean allowsBaseline) {
2642             if (alignment == null) {
2643                 throw new IllegalArgumentException("Alignment must be non-null");
2644             }
2645             if (!allowsBaseline && alignment == Alignment.BASELINE) {
2646                 throw new IllegalArgumentException("Alignment must be one of:" +
2647                         "LEADING, TRAILING or CENTER");
2648             }
2649         }
2650     }
2651 
2652 
2653     /**
2654      * An extension of {@code ParallelGroup} that aligns its
2655      * constituent {@code Spring}s along the baseline.
2656      */
2657     private class BaselineGroup extends ParallelGroup {
2658         // Whether or not all child springs have a baseline
2659         private boolean allSpringsHaveBaseline;
2660 
2661         // max(spring.getBaseline()) of all springs aligned along the baseline
2662         // that have a baseline
2663         private int prefAscent;
2664 
2665         // max(spring.getPreferredSize().height - spring.getBaseline()) of all
2666         // springs aligned along the baseline that have a baseline
2667         private int prefDescent;
2668 
2669         // Whether baselineAnchoredToTop was explicitly set
2670         private boolean baselineAnchorSet;
2671 
2672         // Whether the baseline is anchored to the top or the bottom.
2673         // If anchored to the top the baseline is always at prefAscent,
2674         // otherwise the baseline is at (height - prefDescent)
2675         private boolean baselineAnchoredToTop;
2676 
2677         // Whether or not the baseline has been calculated.
2678         private boolean calcedBaseline;
2679 
2680         BaselineGroup(boolean resizable) {
2681             super(Alignment.LEADING, resizable);
2682             prefAscent = prefDescent = -1;
2683             calcedBaseline = false;
2684         }
2685 
2686         BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) {
2687             this(resizable);
2688             this.baselineAnchoredToTop = baselineAnchoredToTop;
2689             baselineAnchorSet = true;
2690         }
2691 
2692         void unset() {
2693             super.unset();
2694             prefAscent = prefDescent = -1;
2695             calcedBaseline = false;
2696         }
2697 
2698         void setValidSize(int axis, int origin, int size) {
2699             checkAxis(axis);
2700             if (prefAscent == -1) {
2701                 super.setValidSize(axis, origin, size);
2702             } else {
2703                 // do baseline layout
2704                 baselineLayout(origin, size);
2705             }
2706         }
2707 
2708         int calculateSize(int axis, int type) {
2709             checkAxis(axis);
2710             if (!calcedBaseline) {
2711                 calculateBaselineAndResizeBehavior();
2712             }
2713             if (type == MIN_SIZE) {
2714                 return calculateMinSize();
2715             }
2716             if (type == MAX_SIZE) {
2717                 return calculateMaxSize();
2718             }
2719             if (allSpringsHaveBaseline) {
2720                 return prefAscent + prefDescent;
2721             }
2722             return Math.max(prefAscent + prefDescent,
2723                     super.calculateSize(axis, type));
2724         }
2725 
2726         private void calculateBaselineAndResizeBehavior() {
2727             // calculate baseline
2728             prefAscent = 0;
2729             prefDescent = 0;
2730             int baselineSpringCount = 0;
2731             BaselineResizeBehavior resizeBehavior = null;
2732             for (Spring spring : springs) {
2733                 if (spring.getAlignment() == null ||
2734                         spring.getAlignment() == Alignment.BASELINE) {
2735                     int baseline = spring.getBaseline();
2736                     if (baseline >= 0) {
2737                         if (spring.isResizable(VERTICAL)) {
2738                             BaselineResizeBehavior brb = spring.
2739                                     getBaselineResizeBehavior();
2740                             if (resizeBehavior == null) {
2741                                 resizeBehavior = brb;
2742                             } else if (brb != resizeBehavior) {
2743                                 resizeBehavior = BaselineResizeBehavior.
2744                                         CONSTANT_ASCENT;
2745                             }
2746                         }
2747                         prefAscent = Math.max(prefAscent, baseline);
2748                         prefDescent = Math.max(prefDescent, spring.
2749                                 getPreferredSize(VERTICAL) - baseline);
2750                         baselineSpringCount++;
2751                     }
2752                 }
2753             }
2754             if (!baselineAnchorSet) {
2755                 if (resizeBehavior == BaselineResizeBehavior.CONSTANT_DESCENT){
2756                     this.baselineAnchoredToTop = false;
2757                 } else {
2758                     this.baselineAnchoredToTop = true;
2759                 }
2760             }
2761             allSpringsHaveBaseline = (baselineSpringCount == springs.size());
2762             calcedBaseline = true;
2763         }
2764 
2765         private int calculateMaxSize() {
2766             int maxAscent = prefAscent;
2767             int maxDescent = prefDescent;
2768             int nonBaselineMax = 0;
2769             for (Spring spring : springs) {
2770                 int baseline;
2771                 int springMax = spring.getMaximumSize(VERTICAL);
2772                 if ((spring.getAlignment() == null ||
2773                         spring.getAlignment() == Alignment.BASELINE) &&
2774                         (baseline = spring.getBaseline()) >= 0) {
2775                     int springPref = spring.getPreferredSize(VERTICAL);
2776                     if (springPref != springMax) {
2777                         switch (spring.getBaselineResizeBehavior()) {
2778                             case CONSTANT_ASCENT:
2779                                 if (baselineAnchoredToTop) {
2780                                     maxDescent = Math.max(maxDescent,
2781                                             springMax - baseline);
2782                                 }
2783                                 break;
2784                             case CONSTANT_DESCENT:
2785                                 if (!baselineAnchoredToTop) {
2786                                     maxAscent = Math.max(maxAscent,
2787                                             springMax - springPref + baseline);
2788                                 }
2789                                 break;
2790                             default: // CENTER_OFFSET and OTHER, not resizable
2791                                 break;
2792                         }
2793                     }
2794                 } else {
2795                     // Not aligned along the baseline, or no baseline.
2796                     nonBaselineMax = Math.max(nonBaselineMax, springMax);
2797                 }
2798             }
2799             return Math.max(nonBaselineMax, maxAscent + maxDescent);
2800         }
2801 
2802         private int calculateMinSize() {
2803             int minAscent = 0;
2804             int minDescent = 0;
2805             int nonBaselineMin = 0;
2806             if (baselineAnchoredToTop) {
2807                 minAscent = prefAscent;
2808             } else {
2809                 minDescent = prefDescent;
2810             }
2811             for (Spring spring : springs) {
2812                 int springMin = spring.getMinimumSize(VERTICAL);
2813                 int baseline;
2814                 if ((spring.getAlignment() == null ||
2815                         spring.getAlignment() == Alignment.BASELINE) &&
2816                         (baseline = spring.getBaseline()) >= 0) {
2817                     int springPref = spring.getPreferredSize(VERTICAL);
2818                     BaselineResizeBehavior brb = spring.
2819                             getBaselineResizeBehavior();
2820                     switch (brb) {
2821                         case CONSTANT_ASCENT:
2822                             if (baselineAnchoredToTop) {
2823                                 minDescent = Math.max(springMin - baseline,
2824                                         minDescent);
2825                             } else {
2826                                 minAscent = Math.max(baseline, minAscent);
2827                             }
2828                             break;
2829                         case CONSTANT_DESCENT:
2830                             if (!baselineAnchoredToTop) {
2831                                 minAscent = Math.max(
2832                                         baseline - (springPref - springMin),
2833                                         minAscent);
2834                             } else {
2835                                 minDescent = Math.max(springPref - baseline,
2836                                         minDescent);
2837                             }
2838                             break;
2839                         default:
2840                             // CENTER_OFFSET and OTHER are !resizable, use
2841                             // the preferred size.
2842                             minAscent = Math.max(baseline, minAscent);
2843                             minDescent = Math.max(springPref - baseline,
2844                                     minDescent);
2845                             break;
2846                     }
2847                 } else {
2848                     // Not aligned along the baseline, or no baseline.
2849                     nonBaselineMin = Math.max(nonBaselineMin, springMin);
2850                 }
2851             }
2852             return Math.max(nonBaselineMin, minAscent + minDescent);
2853         }
2854 
2855         /**
2856          * Lays out springs that have a baseline along the baseline.  All
2857          * others are centered.
2858          */
2859         private void baselineLayout(int origin, int size) {
2860             int ascent;
2861             int descent;
2862             if (baselineAnchoredToTop) {
2863                 ascent = prefAscent;
2864                 descent = size - ascent;
2865             } else {
2866                 ascent = size - prefDescent;
2867                 descent = prefDescent;
2868             }
2869             for (Spring spring : springs) {
2870                 Alignment alignment = spring.getAlignment();
2871                 if (alignment == null || alignment == Alignment.BASELINE) {
2872                     int baseline = spring.getBaseline();
2873                     if (baseline >= 0) {
2874                         int springMax = spring.getMaximumSize(VERTICAL);
2875                         int springPref = spring.getPreferredSize(VERTICAL);
2876                         int height = springPref;
2877                         int y;
2878                         switch(spring.getBaselineResizeBehavior()) {
2879                             case CONSTANT_ASCENT:
2880                                 y = origin + ascent - baseline;
2881                                 height = Math.min(descent, springMax -
2882                                         baseline) + baseline;
2883                                 break;
2884                             case CONSTANT_DESCENT:
2885                                 height = Math.min(ascent, springMax -
2886                                         springPref + baseline) +
2887                                         (springPref - baseline);
2888                                 y = origin + ascent +
2889                                         (springPref - baseline) - height;
2890                                 break;
2891                             default: // CENTER_OFFSET & OTHER, not resizable
2892                                 y = origin + ascent - baseline;
2893                                 break;
2894                         }
2895                         spring.setSize(VERTICAL, y, height);
2896                     } else {
2897                         setChildSize(spring, VERTICAL, origin, size);
2898                     }
2899                 } else {
2900                     setChildSize(spring, VERTICAL, origin, size);
2901                 }
2902             }
2903         }
2904 
2905         int getBaseline() {
2906             if (springs.size() > 1) {
2907                 // Force the baseline to be calculated
2908                 getPreferredSize(VERTICAL);
2909                 return prefAscent;
2910             } else if (springs.size() == 1) {
2911                 return springs.get(0).getBaseline();
2912             }
2913             return -1;
2914         }
2915 
2916         BaselineResizeBehavior getBaselineResizeBehavior() {
2917             if (springs.size() == 1) {
2918                 return springs.get(0).getBaselineResizeBehavior();
2919             }
2920             if (baselineAnchoredToTop) {
2921                 return BaselineResizeBehavior.CONSTANT_ASCENT;
2922             }
2923             return BaselineResizeBehavior.CONSTANT_DESCENT;
2924         }
2925 
2926         // If the axis is VERTICAL, throws an IllegalStateException
2927         private void checkAxis(int axis) {
2928             if (axis == HORIZONTAL) {
2929                 throw new IllegalStateException(
2930                         "Baseline must be used along vertical axis");
2931             }
2932         }
2933     }
2934 
2935 
2936     private final class ComponentSpring extends Spring {
2937         private Component component;
2938         private int origin;
2939 
2940         // min/pref/max are either a value >= 0 or one of
2941         // DEFAULT_SIZE or PREFERRED_SIZE
2942         private final int min;
2943         private final int pref;
2944         private final int max;
2945 
2946         // Baseline for the component, computed as necessary.
2947         private int baseline = -1;
2948 
2949         // Whether or not the size has been requested yet.
2950         private boolean installed;
2951 
2952         private ComponentSpring(Component component, int min, int pref,
2953                 int max) {
2954             this.component = component;
2955             if (component == null) {
2956                 throw new IllegalArgumentException(
2957                         "Component must be non-null");
2958             }
2959 
2960             checkSize(min, pref, max, true);
2961 
2962             this.min = min;
2963             this.max = max;
2964             this.pref = pref;
2965 
2966             // getComponentInfo makes sure component is a child of the
2967             // Container GroupLayout is the LayoutManager for.
2968             getComponentInfo(component);
2969         }
2970 
2971         int calculateMinimumSize(int axis) {
2972             if (isLinked(axis)) {
2973                 return getLinkSize(axis, MIN_SIZE);
2974             }
2975             return calculateNonlinkedMinimumSize(axis);
2976         }
2977 
2978         int calculatePreferredSize(int axis) {
2979             if (isLinked(axis)) {
2980                 return getLinkSize(axis, PREF_SIZE);
2981             }
2982             int min = getMinimumSize(axis);
2983             int pref = calculateNonlinkedPreferredSize(axis);
2984             int max = getMaximumSize(axis);
2985             return Math.min(max, Math.max(min, pref));
2986         }
2987 
2988         int calculateMaximumSize(int axis) {
2989             if (isLinked(axis)) {
2990                 return getLinkSize(axis, MAX_SIZE);
2991             }
2992             return Math.max(getMinimumSize(axis),
2993                     calculateNonlinkedMaximumSize(axis));
2994         }
2995 
2996         boolean isVisible() {
2997             return getComponentInfo(getComponent()).isVisible();
2998         }
2999 
3000         int calculateNonlinkedMinimumSize(int axis) {
3001             if (!isVisible()) {
3002                 return 0;
3003             }
3004             if (min >= 0) {
3005                 return min;
3006             }
3007             if (min == PREFERRED_SIZE) {
3008                 return calculateNonlinkedPreferredSize(axis);
3009             }
3010             assert (min == DEFAULT_SIZE);
3011             return getSizeAlongAxis(axis, component.getMinimumSize());
3012         }
3013 
3014         int calculateNonlinkedPreferredSize(int axis) {
3015             if (!isVisible()) {
3016                 return 0;
3017             }
3018             if (pref >= 0) {
3019                 return pref;
3020             }
3021             assert (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE);
3022             return getSizeAlongAxis(axis, component.getPreferredSize());
3023         }
3024 
3025         int calculateNonlinkedMaximumSize(int axis) {
3026             if (!isVisible()) {
3027                 return 0;
3028             }
3029             if (max >= 0) {
3030                 return max;
3031             }
3032             if (max == PREFERRED_SIZE) {
3033                 return calculateNonlinkedPreferredSize(axis);
3034             }
3035             assert (max == DEFAULT_SIZE);
3036             return getSizeAlongAxis(axis, component.getMaximumSize());
3037         }
3038 
3039         private int getSizeAlongAxis(int axis, Dimension size) {
3040             return (axis == HORIZONTAL) ? size.width : size.height;
3041         }
3042 
3043         private int getLinkSize(int axis, int type) {
3044             if (!isVisible()) {
3045                 return 0;
3046             }
3047             ComponentInfo ci = getComponentInfo(component);
3048             return ci.getLinkSize(axis, type);
3049         }
3050 
3051         void setSize(int axis, int origin, int size) {
3052             super.setSize(axis, origin, size);
3053             this.origin = origin;
3054             if (size == UNSET) {
3055                 baseline = -1;
3056             }
3057         }
3058 
3059         int getOrigin() {
3060             return origin;
3061         }
3062 
3063         void setComponent(Component component) {
3064             this.component = component;
3065         }
3066 
3067         Component getComponent() {
3068             return component;
3069         }
3070 
3071         int getBaseline() {
3072             if (baseline == -1) {
3073                 Spring horizontalSpring = getComponentInfo(component).
3074                         horizontalSpring;
3075                 int width = horizontalSpring.getPreferredSize(HORIZONTAL);
3076                 int height = getPreferredSize(VERTICAL);
3077                 if (width > 0 && height > 0) {
3078                     baseline = component.getBaseline(width, height);
3079                 }
3080             }
3081             return baseline;
3082         }
3083 
3084         BaselineResizeBehavior getBaselineResizeBehavior() {
3085             return getComponent().getBaselineResizeBehavior();
3086         }
3087 
3088         private boolean isLinked(int axis) {
3089             return getComponentInfo(component).isLinked(axis);
3090         }
3091 
3092         void installIfNecessary(int axis) {
3093             if (!installed) {
3094                 installed = true;
3095                 if (axis == HORIZONTAL) {
3096                     getComponentInfo(component).horizontalSpring = this;
3097                 } else {
3098                     getComponentInfo(component).verticalSpring = this;
3099                 }
3100             }
3101         }
3102 
3103         @Override
3104         boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
3105             return !isVisible();
3106         }
3107     }
3108 
3109 
3110     /**
3111      * Spring representing the preferred distance between two components.
3112      */
3113     private class PreferredGapSpring extends Spring {
3114         private final JComponent source;
3115         private final JComponent target;
3116         private final ComponentPlacement type;
3117         private final int pref;
3118         private final int max;
3119 
3120         PreferredGapSpring(JComponent source, JComponent target,
3121                 ComponentPlacement type, int pref, int max) {
3122             this.source = source;
3123             this.target = target;
3124             this.type = type;
3125             this.pref = pref;
3126             this.max = max;
3127         }
3128 
3129         int calculateMinimumSize(int axis) {
3130             return getPadding(axis);
3131         }
3132 
3133         int calculatePreferredSize(int axis) {
3134             if (pref == DEFAULT_SIZE || pref == PREFERRED_SIZE) {
3135                 return getMinimumSize(axis);
3136             }
3137             int min = getMinimumSize(axis);
3138             int max = getMaximumSize(axis);
3139             return Math.min(max, Math.max(min, pref));
3140         }
3141 
3142         int calculateMaximumSize(int axis) {
3143             if (max == PREFERRED_SIZE || max == DEFAULT_SIZE) {
3144                 return getPadding(axis);
3145             }
3146             return Math.max(getMinimumSize(axis), max);
3147         }
3148 
3149         private int getPadding(int axis) {
3150             int position;
3151             if (axis == HORIZONTAL) {
3152                 position = SwingConstants.EAST;
3153             } else {
3154                 position = SwingConstants.SOUTH;
3155             }
3156             return getLayoutStyle0().getPreferredGap(source,
3157                     target, type, position, host);
3158         }
3159 
3160         @Override
3161         boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
3162             return false;
3163         }
3164     }
3165 
3166 
3167     /**
3168      * Spring represented a certain amount of space.
3169      */
3170     private class GapSpring extends Spring {
3171         private final int min;
3172         private final int pref;
3173         private final int max;
3174 
3175         GapSpring(int min, int pref, int max) {
3176             checkSize(min, pref, max, false);
3177             this.min = min;
3178             this.pref = pref;
3179             this.max = max;
3180         }
3181 
3182         int calculateMinimumSize(int axis) {
3183             if (min == PREFERRED_SIZE) {
3184                 return getPreferredSize(axis);
3185             }
3186             return min;
3187         }
3188 
3189         int calculatePreferredSize(int axis) {
3190             return pref;
3191         }
3192 
3193         int calculateMaximumSize(int axis) {
3194             if (max == PREFERRED_SIZE) {
3195                 return getPreferredSize(axis);
3196             }
3197             return max;
3198         }
3199 
3200         @Override
3201         boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
3202             return false;
3203         }
3204     }
3205 
3206 
3207     /**
3208      * Spring reprensenting the distance between any number of sources and
3209      * targets.  The targets and sources are computed during layout.  An
3210      * instance of this can either be dynamically created when
3211      * autocreatePadding is true, or explicitly created by the developer.
3212      */
3213     private class AutoPreferredGapSpring extends Spring {
3214         List<ComponentSpring> sources;
3215         ComponentSpring source;
3216         private List<AutoPreferredGapMatch> matches;
3217         int size;
3218         int lastSize;
3219         private final int pref;
3220         private final int max;
3221         // Type of gap
3222         private ComponentPlacement type;
3223         private boolean userCreated;
3224 
3225         private AutoPreferredGapSpring() {
3226             this.pref = PREFERRED_SIZE;
3227             this.max = PREFERRED_SIZE;
3228             this.type = ComponentPlacement.RELATED;
3229         }
3230 
3231         AutoPreferredGapSpring(int pref, int max) {
3232             this.pref = pref;
3233             this.max = max;
3234         }
3235 
3236         AutoPreferredGapSpring(ComponentPlacement type, int pref, int max) {
3237             this.type = type;
3238             this.pref = pref;
3239             this.max = max;
3240             this.userCreated = true;
3241         }
3242 
3243         public void setSource(ComponentSpring source) {
3244             this.source = source;
3245         }
3246 
3247         public void setSources(List<ComponentSpring> sources) {
3248             this.sources = new ArrayList<ComponentSpring>(sources);
3249         }
3250 
3251         public void setUserCreated(boolean userCreated) {
3252             this.userCreated = userCreated;
3253         }
3254 
3255         public boolean getUserCreated() {
3256             return userCreated;
3257         }
3258 
3259         void unset() {
3260             lastSize = getSize();
3261             super.unset();
3262             size = 0;
3263         }
3264 
3265         public void reset() {
3266             size = 0;
3267             sources = null;
3268             source = null;
3269             matches = null;
3270         }
3271 
3272         public void calculatePadding(int axis) {
3273             size = UNSET;
3274             int maxPadding = UNSET;
3275             if (matches != null) {
3276                 LayoutStyle p = getLayoutStyle0();
3277                 int position;
3278                 if (axis == HORIZONTAL) {
3279                     if (isLeftToRight()) {
3280                         position = SwingConstants.EAST;
3281                     } else {
3282                         position = SwingConstants.WEST;
3283                     }
3284                 } else {
3285                     position = SwingConstants.SOUTH;
3286                 }
3287                 for (int i = matches.size() - 1; i >= 0; i--) {
3288                     AutoPreferredGapMatch match = matches.get(i);
3289                     maxPadding = Math.max(maxPadding,
3290                             calculatePadding(p, position, match.source,
3291                             match.target));
3292                 }
3293             }
3294             if (size == UNSET) {
3295                 size = 0;
3296             }
3297             if (maxPadding == UNSET) {
3298                 maxPadding = 0;
3299             }
3300             if (lastSize != UNSET) {
3301                 size += Math.min(maxPadding, lastSize);
3302             }
3303         }
3304 
3305         private int calculatePadding(LayoutStyle p, int position,
3306                 ComponentSpring source,
3307                 ComponentSpring target) {
3308             int delta = target.getOrigin() - (source.getOrigin() +
3309                     source.getSize());
3310             if (delta >= 0) {
3311                 int padding;
3312                 if ((source.getComponent() instanceof JComponent) &&
3313                         (target.getComponent() instanceof JComponent)) {
3314                     padding = p.getPreferredGap(
3315                             (JComponent)source.getComponent(),
3316                             (JComponent)target.getComponent(), type, position,
3317                             host);
3318                 } else {
3319                     padding = 10;
3320                 }
3321                 if (padding > delta) {
3322                     size = Math.max(size, padding - delta);
3323                 }
3324                 return padding;
3325             }
3326             return 0;
3327         }
3328 
3329         public void addTarget(ComponentSpring spring, int axis) {
3330             int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL;
3331             if (source != null) {
3332                 if (areParallelSiblings(source.getComponent(),
3333                         spring.getComponent(), oAxis)) {
3334                     addValidTarget(source, spring);
3335                 }
3336             } else {
3337                 Component component = spring.getComponent();
3338                 for (int counter = sources.size() - 1; counter >= 0;
3339                          counter--){
3340                     ComponentSpring source = sources.get(counter);
3341                     if (areParallelSiblings(source.getComponent(),
3342                             component, oAxis)) {
3343                         addValidTarget(source, spring);
3344                     }
3345                 }
3346             }
3347         }
3348 
3349         private void addValidTarget(ComponentSpring source,
3350                 ComponentSpring target) {
3351             if (matches == null) {
3352                 matches = new ArrayList<AutoPreferredGapMatch>(1);
3353             }
3354             matches.add(new AutoPreferredGapMatch(source, target));
3355         }
3356 
3357         int calculateMinimumSize(int axis) {
3358             return size;
3359         }
3360 
3361         int calculatePreferredSize(int axis) {
3362             if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) {
3363                 return size;
3364             }
3365             return Math.max(size, pref);
3366         }
3367 
3368         int calculateMaximumSize(int axis) {
3369             if (max >= 0) {
3370                 return Math.max(getPreferredSize(axis), max);
3371             }
3372             return size;
3373         }
3374 
3375         String getMatchDescription() {
3376             return (matches == null) ? "" : matches.toString();
3377         }
3378 
3379         public String toString() {
3380             return super.toString() + getMatchDescription();
3381         }
3382 
3383         @Override
3384         boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
3385             return treatAutopaddingAsZeroSized;
3386         }
3387     }
3388 
3389 
3390     /**
3391      * Represents two springs that should have autopadding inserted between
3392      * them.
3393      */
3394     private static final class AutoPreferredGapMatch {
3395         public final ComponentSpring source;
3396         public final ComponentSpring target;
3397 
3398         AutoPreferredGapMatch(ComponentSpring source, ComponentSpring target) {
3399             this.source = source;
3400             this.target = target;
3401         }
3402 
3403         private String toString(ComponentSpring spring) {
3404             return spring.getComponent().getName();
3405         }
3406 
3407         public String toString() {
3408             return "[" + toString(source) + "-" + toString(target) + "]";
3409         }
3410     }
3411 
3412 
3413     /**
3414      * An extension of AutopaddingSpring used for container level padding.
3415      */
3416     private class ContainerAutoPreferredGapSpring extends
3417             AutoPreferredGapSpring {
3418         private List<ComponentSpring> targets;
3419 
3420         ContainerAutoPreferredGapSpring() {
3421             super();
3422             setUserCreated(true);
3423         }
3424 
3425         ContainerAutoPreferredGapSpring(int pref, int max) {
3426             super(pref, max);
3427             setUserCreated(true);
3428         }
3429 
3430         public void addTarget(ComponentSpring spring, int axis) {
3431             if (targets == null) {
3432                 targets = new ArrayList<ComponentSpring>(1);
3433             }
3434             targets.add(spring);
3435         }
3436 
3437         public void calculatePadding(int axis) {
3438             LayoutStyle p = getLayoutStyle0();
3439             int maxPadding = 0;
3440             int position;
3441             size = 0;
3442             if (targets != null) {
3443                 // Leading
3444                 if (axis == HORIZONTAL) {
3445                     if (isLeftToRight()) {
3446                         position = SwingConstants.WEST;
3447                     } else {
3448                         position = SwingConstants.EAST;
3449                     }
3450                 } else {
3451                     position = SwingConstants.SOUTH;
3452                 }
3453                 for (int i = targets.size() - 1; i >= 0; i--) {
3454                     ComponentSpring targetSpring = targets.get(i);
3455                     int padding = 10;
3456                     if (targetSpring.getComponent() instanceof JComponent) {
3457                         padding = p.getContainerGap(
3458                                 (JComponent)targetSpring.getComponent(),
3459                                 position, host);
3460                         maxPadding = Math.max(padding, maxPadding);
3461                         padding -= targetSpring.getOrigin();
3462                     } else {
3463                         maxPadding = Math.max(padding, maxPadding);
3464                     }
3465                     size = Math.max(size, padding);
3466                 }
3467             } else {
3468                 // Trailing
3469                 if (axis == HORIZONTAL) {
3470                     if (isLeftToRight()) {
3471                         position = SwingConstants.EAST;
3472                     } else {
3473                         position = SwingConstants.WEST;
3474                     }
3475                 } else {
3476                     position = SwingConstants.SOUTH;
3477                 }
3478                 if (sources != null) {
3479                     for (int i = sources.size() - 1; i >= 0; i--) {
3480                         ComponentSpring sourceSpring = sources.get(i);
3481                         maxPadding = Math.max(maxPadding,
3482                                 updateSize(p, sourceSpring, position));
3483                     }
3484                 } else if (source != null) {
3485                     maxPadding = updateSize(p, source, position);
3486                 }
3487             }
3488             if (lastSize != UNSET) {
3489                 size += Math.min(maxPadding, lastSize);
3490             }
3491         }
3492 
3493         private int updateSize(LayoutStyle p, ComponentSpring sourceSpring,
3494                 int position) {
3495             int padding = 10;
3496             if (sourceSpring.getComponent() instanceof JComponent) {
3497                 padding = p.getContainerGap(
3498                         (JComponent)sourceSpring.getComponent(), position,
3499                         host);
3500             }
3501             int delta = Math.max(0, getParent().getSize() -
3502                     sourceSpring.getSize() - sourceSpring.getOrigin());
3503             size = Math.max(size, padding - delta);
3504             return padding;
3505         }
3506 
3507         String getMatchDescription() {
3508             if (targets != null) {
3509                 return "leading: " + targets.toString();
3510             }
3511             if (sources != null) {
3512                 return "trailing: " + sources.toString();
3513             }
3514             return "--";
3515         }
3516     }
3517 
3518 
3519     // LinkInfo contains the set of ComponentInfosthat are linked along a
3520     // particular axis.
3521     private static class LinkInfo {
3522         private final int axis;
3523         private final List<ComponentInfo> linked;
3524         private int size;
3525 
3526         LinkInfo(int axis) {
3527             linked = new ArrayList<ComponentInfo>();
3528             size = UNSET;
3529             this.axis = axis;
3530         }
3531 
3532         public void add(ComponentInfo child) {
3533             LinkInfo childMaster = child.getLinkInfo(axis, false);
3534             if (childMaster == null) {
3535                 linked.add(child);
3536                 child.setLinkInfo(axis, this);
3537             } else if (childMaster != this) {
3538                 linked.addAll(childMaster.linked);
3539                 for (ComponentInfo childInfo : childMaster.linked) {
3540                     childInfo.setLinkInfo(axis, this);
3541                 }
3542             }
3543             clearCachedSize();
3544         }
3545 
3546         public void remove(ComponentInfo info) {
3547             linked.remove(info);
3548             info.setLinkInfo(axis, null);
3549             if (linked.size() == 1) {
3550                 linked.get(0).setLinkInfo(axis, null);
3551             }
3552             clearCachedSize();
3553         }
3554 
3555         public void clearCachedSize() {
3556             size = UNSET;
3557         }
3558 
3559         public int getSize(int axis) {
3560             if (size == UNSET) {
3561                 size = calculateLinkedSize(axis);
3562             }
3563             return size;
3564         }
3565 
3566         private int calculateLinkedSize(int axis) {
3567             int size = 0;
3568             for (ComponentInfo info : linked) {
3569                 ComponentSpring spring;
3570                 if (axis == HORIZONTAL) {
3571                     spring = info.horizontalSpring;
3572                 } else {
3573                     assert (axis == VERTICAL);
3574                     spring = info.verticalSpring;
3575                 }
3576                 size = Math.max(size,
3577                         spring.calculateNonlinkedPreferredSize(axis));
3578             }
3579             return size;
3580         }
3581     }
3582 
3583     /**
3584      * Tracks the horizontal/vertical Springs for a Component.
3585      * This class is also used to handle Springs that have their sizes
3586      * linked.
3587      */
3588     private class ComponentInfo {
3589         // Component being layed out
3590         private Component component;
3591 
3592         ComponentSpring horizontalSpring;
3593         ComponentSpring verticalSpring;
3594 
3595         // If the component's size is linked to other components, the
3596         // horizontalMaster and/or verticalMaster reference the group of
3597         // linked components.
3598         private LinkInfo horizontalMaster;
3599         private LinkInfo verticalMaster;
3600 
3601         private boolean visible;
3602         private Boolean honorsVisibility;
3603 
3604         ComponentInfo(Component component) {
3605             this.component = component;
3606             updateVisibility();
3607         }
3608 
3609         public void dispose() {
3610             // Remove horizontal/vertical springs
3611             removeSpring(horizontalSpring);
3612             horizontalSpring = null;
3613             removeSpring(verticalSpring);
3614             verticalSpring = null;
3615             // Clean up links
3616             if (horizontalMaster != null) {
3617                 horizontalMaster.remove(this);
3618             }
3619             if (verticalMaster != null) {
3620                 verticalMaster.remove(this);
3621             }
3622         }
3623 
3624         void setHonorsVisibility(Boolean honorsVisibility) {
3625             this.honorsVisibility = honorsVisibility;
3626         }
3627 
3628         private void removeSpring(Spring spring) {
3629             if (spring != null) {
3630                 ((Group)spring.getParent()).springs.remove(spring);
3631             }
3632         }
3633 
3634         public boolean isVisible() {
3635             return visible;
3636         }
3637 
3638         /**
3639          * Updates the cached visibility.
3640          *
3641          * @return true if the visibility changed
3642          */
3643         boolean updateVisibility() {
3644             boolean honorsVisibility;
3645             if (this.honorsVisibility == null) {
3646                 honorsVisibility = GroupLayout.this.getHonorsVisibility();
3647             } else {
3648                 honorsVisibility = this.honorsVisibility;
3649             }
3650             boolean newVisible = (honorsVisibility) ?
3651                 component.isVisible() : true;
3652             if (visible != newVisible) {
3653                 visible = newVisible;
3654                 return true;
3655             }
3656             return false;
3657         }
3658 
3659         public void setBounds(Insets insets, int parentWidth, boolean ltr) {
3660             int x = horizontalSpring.getOrigin();
3661             int w = horizontalSpring.getSize();
3662             int y = verticalSpring.getOrigin();
3663             int h = verticalSpring.getSize();
3664 
3665             if (!ltr) {
3666                 x = parentWidth - x - w;
3667             }
3668             component.setBounds(x + insets.left, y + insets.top, w, h);
3669         }
3670 
3671         public void setComponent(Component component) {
3672             this.component = component;
3673             if (horizontalSpring != null) {
3674                 horizontalSpring.setComponent(component);
3675             }
3676             if (verticalSpring != null) {
3677                 verticalSpring.setComponent(component);
3678             }
3679         }
3680 
3681         public Component getComponent() {
3682             return component;
3683         }
3684 
3685         /**
3686          * Returns true if this component has its size linked to
3687          * other components.
3688          */
3689         public boolean isLinked(int axis) {
3690             if (axis == HORIZONTAL) {
3691                 return horizontalMaster != null;
3692             }
3693             assert (axis == VERTICAL);
3694             return (verticalMaster != null);
3695         }
3696 
3697         private void setLinkInfo(int axis, LinkInfo linkInfo) {
3698             if (axis == HORIZONTAL) {
3699                 horizontalMaster = linkInfo;
3700             } else {
3701                 assert (axis == VERTICAL);
3702                 verticalMaster = linkInfo;
3703             }
3704         }
3705 
3706         public LinkInfo getLinkInfo(int axis) {
3707             return getLinkInfo(axis, true);
3708         }
3709 
3710         private LinkInfo getLinkInfo(int axis, boolean create) {
3711             if (axis == HORIZONTAL) {
3712                 if (horizontalMaster == null && create) {
3713                     // horizontalMaster field is directly set by adding
3714                     // us to the LinkInfo.
3715                     new LinkInfo(HORIZONTAL).add(this);
3716                 }
3717                 return horizontalMaster;
3718             } else {
3719                 assert (axis == VERTICAL);
3720                 if (verticalMaster == null && create) {
3721                     // verticalMaster field is directly set by adding
3722                     // us to the LinkInfo.
3723                     new LinkInfo(VERTICAL).add(this);
3724                 }
3725                 return verticalMaster;
3726             }
3727         }
3728 
3729         public void clearCachedSize() {
3730             if (horizontalMaster != null) {
3731                 horizontalMaster.clearCachedSize();
3732             }
3733             if (verticalMaster != null) {
3734                 verticalMaster.clearCachedSize();
3735             }
3736         }
3737 
3738         int getLinkSize(int axis, int type) {
3739             if (axis == HORIZONTAL) {
3740                 return horizontalMaster.getSize(axis);
3741             } else {
3742                 assert (axis == VERTICAL);
3743                 return verticalMaster.getSize(axis);
3744             }
3745         }
3746 
3747     }
3748 }