1 /*
   2  * Copyright (c) 2001, 2006, 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.FontMetrics;
  31 import java.awt.Insets;
  32 import java.awt.LayoutManager2;
  33 import java.awt.Rectangle;
  34 import java.util.*;
  35 
  36 /**
  37  * A <code>SpringLayout</code> lays out the children of its associated container
  38  * according to a set of constraints.
  39  * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a>
  40  * in <em>The Java Tutorial</em> for examples of using
  41  * <code>SpringLayout</code>.
  42  *
  43  * <p>
  44  * Each constraint,
  45  * represented by a <code>Spring</code> object,
  46  * controls the vertical or horizontal distance
  47  * between two component edges.
  48  * The edges can belong to
  49  * any child of the container,
  50  * or to the container itself.
  51  * For example,
  52  * the allowable width of a component
  53  * can be expressed using a constraint
  54  * that controls the distance between the west (left) and east (right)
  55  * edges of the component.
  56  * The allowable <em>y</em> coordinates for a component
  57  * can be expressed by constraining the distance between
  58  * the north (top) edge of the component
  59  * and the north edge of its container.
  60  *
  61  * <P>
  62  * Every child of a <code>SpringLayout</code>-controlled container,
  63  * as well as the container itself,
  64  * has exactly one set of constraints
  65  * associated with it.
  66  * These constraints are represented by
  67  * a <code>SpringLayout.Constraints</code> object.
  68  * By default,
  69  * <code>SpringLayout</code> creates constraints
  70  * that make their associated component
  71  * have the minimum, preferred, and maximum sizes
  72  * returned by the component's
  73  * {@link java.awt.Component#getMinimumSize},
  74  * {@link java.awt.Component#getPreferredSize}, and
  75  * {@link java.awt.Component#getMaximumSize}
  76  * methods. The <em>x</em> and <em>y</em> positions are initially not
  77  * constrained, so that until you constrain them the <code>Component</code>
  78  * will be positioned at 0,0 relative to the <code>Insets</code> of the
  79  * parent <code>Container</code>.
  80  *
  81  * <p>
  82  * You can change
  83  * a component's constraints in several ways.
  84  * You can
  85  * use one of the
  86  * {@link #putConstraint putConstraint}
  87  * methods
  88  * to establish a spring
  89  * linking the edges of two components within the same container.
  90  * Or you can get the appropriate <code>SpringLayout.Constraints</code>
  91  * object using
  92  * {@link #getConstraints getConstraints}
  93  * and then modify one or more of its springs.
  94  * Or you can get the spring for a particular edge of a component
  95  * using {@link #getConstraint getConstraint},
  96  * and modify it.
  97  * You can also associate
  98  * your own <code>SpringLayout.Constraints</code> object
  99  * with a component by specifying the constraints object
 100  * when you add the component to its container
 101  * (using
 102  * {@link Container#add(Component, Object)}).
 103  *
 104  * <p>
 105  * The <code>Spring</code> object representing each constraint
 106  * has a minimum, preferred, maximum, and current value.
 107  * The current value of the spring
 108  * is somewhere between the minimum and maximum values,
 109  * according to the formula given in the
 110  * {@link Spring#sum} method description.
 111  * When the minimum, preferred, and maximum values are the same,
 112  * the current value is always equal to them;
 113  * this inflexible spring is called a <em>strut</em>.
 114  * You can create struts using the factory method
 115  * {@link Spring#constant(int)}.
 116  * The <code>Spring</code> class also provides factory methods
 117  * for creating other kinds of springs,
 118  * including springs that depend on other springs.
 119  *
 120  * <p>
 121  * In a <code>SpringLayout</code>, the position of each edge is dependent on
 122  * the position of just one other edge. If a constraint is subsequently added
 123  * to create a new binding for an edge, the previous binding is discarded
 124  * and the edge remains dependent on a single edge.
 125  * Springs should only be attached
 126  * between edges of the container and its immediate children; the behavior
 127  * of the <code>SpringLayout</code> when presented with constraints linking
 128  * the edges of components from different containers (either internal or
 129  * external) is undefined.
 130  *
 131  * <h3>
 132  * SpringLayout vs. Other Layout Managers
 133  * </h3>
 134  *
 135  * <blockquote>
 136  * <hr>
 137  * <strong>Note:</strong>
 138  * Unlike many layout managers,
 139  * <code>SpringLayout</code> doesn't automatically set the location of
 140  * the components it manages.
 141  * If you hand-code a GUI that uses <code>SpringLayout</code>,
 142  * remember to initialize component locations by constraining the west/east
 143  * and north/south locations.
 144  * <p>
 145  * Depending on the constraints you use,
 146  * you may also need to set the size of the container explicitly.
 147  * <hr>
 148  * </blockquote>
 149  *
 150  * <p>
 151  * Despite the simplicity of <code>SpringLayout</code>,
 152  * it can emulate the behavior of most other layout managers.
 153  * For some features,
 154  * such as the line breaking provided by <code>FlowLayout</code>,
 155  * you'll need to
 156  * create a special-purpose subclass of the <code>Spring</code> class.
 157  *
 158  * <p>
 159  * <code>SpringLayout</code> also provides a way to solve
 160  * many of the difficult layout
 161  * problems that cannot be solved by nesting combinations
 162  * of <code>Box</code>es. That said, <code>SpringLayout</code> honors the
 163  * <code>LayoutManager2</code> contract correctly and so can be nested with
 164  * other layout managers -- a technique that can be preferable to
 165  * creating the constraints implied by the other layout managers.
 166  * <p>
 167  * The asymptotic complexity of the layout operation of a <code>SpringLayout</code>
 168  * is linear in the number of constraints (and/or components).
 169  * <p>
 170  * <strong>Warning:</strong>
 171  * Serialized objects of this class will not be compatible with
 172  * future Swing releases. The current serialization support is
 173  * appropriate for short term storage or RMI between applications running
 174  * the same version of Swing.  As of 1.4, support for long term storage
 175  * of all JavaBeans<sup><font size="-2">TM</font></sup>
 176  * has been added to the <code>java.beans</code> package.
 177  * Please see {@link java.beans.XMLEncoder}.
 178  *
 179  * @see Spring
 180  * @see SpringLayout.Constraints
 181  *
 182  * @author      Philip Milne
 183  * @author      Scott Violet
 184  * @author      Joe Winchester
 185  * @since       1.4
 186  */
 187 public class SpringLayout implements LayoutManager2 {
 188     private Map componentConstraints = new HashMap();
 189 
 190     private Spring cyclicReference = Spring.constant(Spring.UNSET);
 191     private Set cyclicSprings;
 192     private Set acyclicSprings;
 193 
 194 
 195     /**
 196      * Specifies the top edge of a component's bounding rectangle.
 197      */
 198     public static final String NORTH  = "North";
 199 
 200     /**
 201      * Specifies the bottom edge of a component's bounding rectangle.
 202      */
 203     public static final String SOUTH  = "South";
 204 
 205     /**
 206      * Specifies the right edge of a component's bounding rectangle.
 207      */
 208     public static final String EAST   = "East";
 209 
 210     /**
 211      * Specifies the left edge of a component's bounding rectangle.
 212      */
 213     public static final String WEST   = "West";
 214 
 215     /**
 216      * Specifies the horizontal center of a component's bounding rectangle.
 217      *
 218      * @since 1.6
 219      */
 220     public static final String HORIZONTAL_CENTER   = "HorizontalCenter";
 221 
 222     /**
 223      * Specifies the vertical center of a component's bounding rectangle.
 224      *
 225      * @since 1.6
 226      */
 227     public static final String VERTICAL_CENTER   = "VerticalCenter";
 228 
 229     /**
 230      * Specifies the baseline of a component.
 231      *
 232      * @since 1.6
 233      */
 234     public static final String BASELINE   = "Baseline";
 235 
 236     /**
 237      * Specifies the width of a component's bounding rectangle.
 238      *
 239      * @since 1.6
 240      */
 241     public static final String WIDTH = "Width";
 242 
 243     /**
 244      * Specifies the height of a component's bounding rectangle.
 245      *
 246      * @since 1.6
 247      */
 248     public static final String HEIGHT = "Height";
 249 
 250     private static String[] ALL_HORIZONTAL = {WEST, WIDTH, EAST, HORIZONTAL_CENTER};
 251 
 252     private static String[] ALL_VERTICAL = {NORTH, HEIGHT, SOUTH, VERTICAL_CENTER, BASELINE};
 253 
 254     /**
 255      * A <code>Constraints</code> object holds the
 256      * constraints that govern the way a component's size and position
 257      * change in a container controlled by a <code>SpringLayout</code>.
 258      * A <code>Constraints</code> object is
 259      * like a <code>Rectangle</code>, in that it
 260      * has <code>x</code>, <code>y</code>,
 261      * <code>width</code>, and <code>height</code> properties.
 262      * In the <code>Constraints</code> object, however,
 263      * these properties have
 264      * <code>Spring</code> values instead of integers.
 265      * In addition,
 266      * a <code>Constraints</code> object
 267      * can be manipulated as four edges
 268      * -- north, south, east, and west --
 269      * using the <code>constraint</code> property.
 270      *
 271      * <p>
 272      * The following formulas are always true
 273      * for a <code>Constraints</code> object (here WEST and <code>x</code> are synonyms, as are and NORTH and <code>y</code>):
 274      *
 275      * <pre>
 276      *               EAST = WEST + WIDTH
 277      *              SOUTH = NORTH + HEIGHT
 278      *  HORIZONTAL_CENTER = WEST + WIDTH/2
 279      *    VERTICAL_CENTER = NORTH + HEIGHT/2
 280      *  ABSOLUTE_BASELINE = NORTH + RELATIVE_BASELINE*
 281      * </pre>
 282      * <p>
 283      * For example, if you have specified the WIDTH and WEST (X) location
 284      * the EAST is calculated as WEST + WIDTH.  If you instead specified
 285      * the WIDTH and EAST locations the WEST (X) location is then calculated
 286      * as EAST - WIDTH.
 287      * <p>
 288      * [RELATIVE_BASELINE is a private constraint that is set automatically when
 289      * the SpringLayout.Constraints(Component) constuctor is called or when
 290      * a constraints object is registered with a SpringLayout object.]
 291      * <p>
 292      * <b>Note</b>: In this document,
 293      * operators represent methods
 294      * in the <code>Spring</code> class.
 295      * For example, "a + b" is equal to
 296      * <code>Spring.sum(a, b)</code>,
 297      * and "a - b" is equal to
 298      * <code>Spring.sum(a, Spring.minus(b))</code>.
 299      * See the
 300      * {@link Spring <code>Spring</code> API documentation}
 301      * for further details
 302      * of spring arithmetic.
 303      *
 304      * <p>
 305      *
 306      * Because a <code>Constraints</code> object's properties --
 307      * representing its edges, size, and location -- can all be set
 308      * independently and yet are interrelated,
 309      * a <code>Constraints</code> object can become <em>over-constrained</em>.
 310      * For example, if the <code>WEST</code>, <code>WIDTH</code> and
 311      * <code>EAST</code> edges are all set, steps must be taken to ensure that
 312      * the first of the formulas above holds.  To do this, the
 313      * <code>Constraints</code>
 314      * object throws away the <em>least recently set</em>
 315      * constraint so as to make the formulas hold.
 316      * @since 1.4
 317      */
 318     public static class Constraints {
 319        private Spring x;
 320        private Spring y;
 321        private Spring width;
 322        private Spring height;
 323        private Spring east;
 324        private Spring south;
 325         private Spring horizontalCenter;
 326         private Spring verticalCenter;
 327         private Spring baseline;
 328 
 329         private List<String> horizontalHistory = new ArrayList<String>(2);
 330         private List<String> verticalHistory = new ArrayList<String>(2);
 331 
 332         // Used for baseline calculations
 333         private Component c;
 334 
 335        /**
 336         * Creates an empty <code>Constraints</code> object.
 337         */
 338        public Constraints() {
 339        }
 340 
 341        /**
 342         * Creates a <code>Constraints</code> object with the
 343         * specified values for its
 344         * <code>x</code> and <code>y</code> properties.
 345         * The <code>height</code> and <code>width</code> springs
 346         * have <code>null</code> values.
 347         *
 348         * @param x  the spring controlling the component's <em>x</em> value
 349         * @param y  the spring controlling the component's <em>y</em> value
 350         */
 351        public Constraints(Spring x, Spring y) {
 352            setX(x);
 353            setY(y);
 354        }
 355 
 356        /**
 357         * Creates a <code>Constraints</code> object with the
 358         * specified values for its
 359         * <code>x</code>, <code>y</code>, <code>width</code>,
 360         * and <code>height</code> properties.
 361         * Note: If the <code>SpringLayout</code> class
 362         * encounters <code>null</code> values in the
 363         * <code>Constraints</code> object of a given component,
 364         * it replaces them with suitable defaults.
 365         *
 366         * @param x  the spring value for the <code>x</code> property
 367         * @param y  the spring value for the <code>y</code> property
 368         * @param width  the spring value for the <code>width</code> property
 369         * @param height  the spring value for the <code>height</code> property
 370         */
 371        public Constraints(Spring x, Spring y, Spring width, Spring height) {
 372            setX(x);
 373            setY(y);
 374            setWidth(width);
 375            setHeight(height);
 376        }
 377 
 378         /**
 379          * Creates a <code>Constraints</code> object with
 380          * suitable <code>x</code>, <code>y</code>, <code>width</code> and
 381          * <code>height</code> springs for component, <code>c</code>.
 382          * The <code>x</code> and <code>y</code> springs are constant
 383          * springs  initialised with the component's location at
 384          * the time this method is called. The <code>width</code> and
 385          * <code>height</code> springs are special springs, created by
 386          * the <code>Spring.width()</code> and <code>Spring.height()</code>
 387          * methods, which track the size characteristics of the component
 388          * when they change.
 389          *
 390          * @param c  the component whose characteristics will be reflected by this Constraints object
 391          * @throws NullPointerException if <code>c</code> is null.
 392          * @since 1.5
 393          */
 394         public Constraints(Component c) {
 395             this.c = c;
 396             setX(Spring.constant(c.getX()));
 397             setY(Spring.constant(c.getY()));
 398             setWidth(Spring.width(c));
 399             setHeight(Spring.height(c));
 400         }
 401 
 402         private void pushConstraint(String name, Spring value, boolean horizontal) {
 403             boolean valid = true;
 404             List<String> history = horizontal ? horizontalHistory :
 405                                                 verticalHistory;
 406             if (history.contains(name)) {
 407                 history.remove(name);
 408                 valid = false;
 409             } else if (history.size() == 2 && value != null) {
 410                 history.remove(0);
 411                 valid = false;
 412             }
 413             if (value != null) {
 414                 history.add(name);
 415             }
 416             if (!valid) {
 417                 String[] all = horizontal ? ALL_HORIZONTAL : ALL_VERTICAL;
 418                 for (int i = 0; i < all.length; i++) {
 419                     String s = all[i];
 420                     if (!history.contains(s)) {
 421                         setConstraint(s, null);
 422                     }
 423                 }
 424             }
 425         }
 426 
 427        private Spring sum(Spring s1, Spring s2) {
 428            return (s1 == null || s2 == null) ? null : Spring.sum(s1, s2);
 429        }
 430 
 431        private Spring difference(Spring s1, Spring s2) {
 432            return (s1 == null || s2 == null) ? null : Spring.difference(s1, s2);
 433        }
 434 
 435         private Spring scale(Spring s, float factor) {
 436             return (s == null) ? null : Spring.scale(s, factor);
 437         }
 438 
 439         private int getBaselineFromHeight(int height) {
 440             if (height < 0) {
 441                 // Bad Scott, Bad Scott!
 442                 return -c.getBaseline(c.getPreferredSize().width,
 443                                       -height);
 444             }
 445             return c.getBaseline(c.getPreferredSize().width, height);
 446         }
 447 
 448         private int getHeightFromBaseLine(int baseline) {
 449             Dimension prefSize = c.getPreferredSize();
 450             int prefHeight = prefSize.height;
 451             int prefBaseline = c.getBaseline(prefSize.width, prefHeight);
 452             if (prefBaseline == baseline) {
 453                 // If prefBaseline < 0, then no baseline, assume preferred
 454                 // height.
 455                 // If prefBaseline == baseline, then specified baseline
 456                 // matches preferred baseline, return preferred height
 457                 return prefHeight;
 458             }
 459             // Valid baseline
 460             switch(c.getBaselineResizeBehavior()) {
 461             case CONSTANT_DESCENT:
 462                 return prefHeight + (baseline - prefBaseline);
 463             case CENTER_OFFSET:
 464                 return prefHeight + 2 * (baseline - prefBaseline);
 465             case CONSTANT_ASCENT:
 466                 // Component baseline and specified baseline will NEVER
 467                 // match, fall through to default
 468             default: // OTHER
 469                 // No way to map from baseline to height.
 470             }
 471             return Integer.MIN_VALUE;
 472         }
 473 
 474          private Spring heightToRelativeBaseline(Spring s) {
 475             return new Spring.SpringMap(s) {
 476                  protected int map(int i) {
 477                     return getBaselineFromHeight(i);
 478                  }
 479 
 480                  protected int inv(int i) {
 481                      return getHeightFromBaseLine(i);
 482                  }
 483             };
 484         }
 485 
 486         private Spring relativeBaselineToHeight(Spring s) {
 487             return new Spring.SpringMap(s) {
 488                 protected int map(int i) {
 489                     return getHeightFromBaseLine(i);
 490                  }
 491 
 492                  protected int inv(int i) {
 493                     return getBaselineFromHeight(i);
 494                  }
 495             };
 496         }
 497 
 498         private boolean defined(List history, String s1, String s2) {
 499             return history.contains(s1) && history.contains(s2);
 500         }
 501 
 502        /**
 503         * Sets the <code>x</code> property,
 504         * which controls the <code>x</code> value
 505         * of a component's location.
 506         *
 507         * @param x the spring controlling the <code>x</code> value
 508         *          of a component's location
 509         *
 510         * @see #getX
 511         * @see SpringLayout.Constraints
 512         */
 513        public void setX(Spring x) {
 514            this.x = x;
 515            pushConstraint(WEST, x, true);
 516        }
 517 
 518        /**
 519         * Returns the value of the <code>x</code> property.
 520         *
 521         * @return the spring controlling the <code>x</code> value
 522         *         of a component's location
 523         *
 524         * @see #setX
 525         * @see SpringLayout.Constraints
 526         */
 527        public Spring getX() {
 528            if (x == null) {
 529                if (defined(horizontalHistory, EAST, WIDTH)) {
 530                    x = difference(east, width);
 531                } else if (defined(horizontalHistory, HORIZONTAL_CENTER, WIDTH)) {
 532                    x = difference(horizontalCenter, scale(width, 0.5f));
 533                } else if (defined(horizontalHistory, HORIZONTAL_CENTER, EAST)) {
 534                    x = difference(scale(horizontalCenter, 2f), east);
 535                }
 536            }
 537            return x;
 538        }
 539 
 540        /**
 541         * Sets the <code>y</code> property,
 542         * which controls the <code>y</code> value
 543         * of a component's location.
 544         *
 545         * @param y the spring controlling the <code>y</code> value
 546         *          of a component's location
 547         *
 548         * @see #getY
 549         * @see SpringLayout.Constraints
 550         */
 551        public void setY(Spring y) {
 552            this.y = y;
 553            pushConstraint(NORTH, y, false);
 554        }
 555 
 556        /**
 557         * Returns the value of the <code>y</code> property.
 558         *
 559         * @return the spring controlling the <code>y</code> value
 560         *         of a component's location
 561         *
 562         * @see #setY
 563         * @see SpringLayout.Constraints
 564         */
 565        public Spring getY() {
 566            if (y == null) {
 567                if (defined(verticalHistory, SOUTH, HEIGHT)) {
 568                    y = difference(south, height);
 569                } else if (defined(verticalHistory, VERTICAL_CENTER, HEIGHT)) {
 570                    y = difference(verticalCenter, scale(height, 0.5f));
 571                } else if (defined(verticalHistory, VERTICAL_CENTER, SOUTH)) {
 572                    y = difference(scale(verticalCenter, 2f), south);
 573                } else if (defined(verticalHistory, BASELINE, HEIGHT)) {
 574                    y = difference(baseline, heightToRelativeBaseline(height));
 575                } else if (defined(verticalHistory, BASELINE, SOUTH)) {
 576                    y = scale(difference(baseline, heightToRelativeBaseline(south)), 2f);
 577 /*
 578                } else if (defined(verticalHistory, BASELINE, VERTICAL_CENTER)) {
 579                    y = scale(difference(baseline, heightToRelativeBaseline(scale(verticalCenter, 2))), 1f/(1-2*0.5f));
 580 */
 581                }
 582            }
 583            return y;
 584        }
 585 
 586        /**
 587         * Sets the <code>width</code> property,
 588         * which controls the width of a component.
 589         *
 590         * @param width the spring controlling the width of this
 591         * <code>Constraints</code> object
 592         *
 593         * @see #getWidth
 594         * @see SpringLayout.Constraints
 595         */
 596        public void setWidth(Spring width) {
 597            this.width = width;
 598            pushConstraint(WIDTH, width, true);
 599        }
 600 
 601        /**
 602         * Returns the value of the <code>width</code> property.
 603         *
 604         * @return the spring controlling the width of a component
 605         *
 606         * @see #setWidth
 607         * @see SpringLayout.Constraints
 608         */
 609        public Spring getWidth() {
 610            if (width == null) {
 611                if (horizontalHistory.contains(EAST)) {
 612                    width = difference(east, getX());
 613                } else if (horizontalHistory.contains(HORIZONTAL_CENTER)) {
 614                    width = scale(difference(horizontalCenter, getX()), 2f);
 615                }
 616            }
 617            return width;
 618        }
 619 
 620        /**
 621         * Sets the <code>height</code> property,
 622         * which controls the height of a component.
 623         *
 624         * @param height the spring controlling the height of this <code>Constraints</code>
 625         * object
 626         *
 627         * @see #getHeight
 628         * @see SpringLayout.Constraints
 629         */
 630        public void setHeight(Spring height) {
 631            this.height = height;
 632            pushConstraint(HEIGHT, height, false);
 633        }
 634 
 635        /**
 636         * Returns the value of the <code>height</code> property.
 637         *
 638         * @return the spring controlling the height of a component
 639         *
 640         * @see #setHeight
 641         * @see SpringLayout.Constraints
 642         */
 643        public Spring getHeight() {
 644            if (height == null) {
 645                if (verticalHistory.contains(SOUTH)) {
 646                    height = difference(south, getY());
 647                } else if (verticalHistory.contains(VERTICAL_CENTER)) {
 648                    height = scale(difference(verticalCenter, getY()), 2f);
 649                } else if (verticalHistory.contains(BASELINE)) {
 650                    height = relativeBaselineToHeight(difference(baseline, getY()));
 651                }
 652            }
 653            return height;
 654        }
 655 
 656        private void setEast(Spring east) {
 657            this.east = east;
 658            pushConstraint(EAST, east, true);
 659        }
 660 
 661        private Spring getEast() {
 662            if (east == null) {
 663                east = sum(getX(), getWidth());
 664            }
 665            return east;
 666        }
 667 
 668        private void setSouth(Spring south) {
 669            this.south = south;
 670            pushConstraint(SOUTH, south, false);
 671        }
 672 
 673        private Spring getSouth() {
 674            if (south == null) {
 675                south = sum(getY(), getHeight());
 676            }
 677            return south;
 678        }
 679 
 680         private Spring getHorizontalCenter() {
 681             if (horizontalCenter == null) {
 682                 horizontalCenter = sum(getX(), scale(getWidth(), 0.5f));
 683             }
 684             return horizontalCenter;
 685         }
 686 
 687         private void setHorizontalCenter(Spring horizontalCenter) {
 688             this.horizontalCenter = horizontalCenter;
 689             pushConstraint(HORIZONTAL_CENTER, horizontalCenter, true);
 690         }
 691 
 692         private Spring getVerticalCenter() {
 693             if (verticalCenter == null) {
 694                 verticalCenter = sum(getY(), scale(getHeight(), 0.5f));
 695             }
 696             return verticalCenter;
 697         }
 698 
 699         private void setVerticalCenter(Spring verticalCenter) {
 700             this.verticalCenter = verticalCenter;
 701             pushConstraint(VERTICAL_CENTER, verticalCenter, false);
 702         }
 703 
 704         private Spring getBaseline() {
 705             if (baseline == null) {
 706                 baseline = sum(getY(), heightToRelativeBaseline(getHeight()));
 707             }
 708             return baseline;
 709         }
 710 
 711         private void setBaseline(Spring baseline) {
 712             this.baseline = baseline;
 713             pushConstraint(BASELINE, baseline, false);
 714         }
 715 
 716        /**
 717         * Sets the spring controlling the specified edge.
 718         * The edge must have one of the following values:
 719         * <code>SpringLayout.NORTH</code>,
 720         * <code>SpringLayout.SOUTH</code>,
 721         * <code>SpringLayout.EAST</code>,
 722         * <code>SpringLayout.WEST</code>,
 723         * <code>SpringLayout.HORIZONTAL_CENTER</code>,
 724         * <code>SpringLayout.VERTICAL_CENTER</code>,
 725         * <code>SpringLayout.BASELINE</code>,
 726         * <code>SpringLayout.WIDTH</code> or
 727         * <code>SpringLayout.HEIGHT</code>.
 728         * For any other <code>String</code> value passed as the edge,
 729         * no action is taken. For a <code>null</code> edge, a
 730         * <code>NullPointerException</code> is thrown.
 731         *
 732         * @param edgeName the edge to be set
 733         * @param s the spring controlling the specified edge
 734         *
 735         * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
 736         *
 737         * @see #getConstraint
 738         * @see #NORTH
 739         * @see #SOUTH
 740         * @see #EAST
 741         * @see #WEST
 742         * @see #HORIZONTAL_CENTER
 743         * @see #VERTICAL_CENTER
 744         * @see #BASELINE
 745         * @see #WIDTH
 746         * @see #HEIGHT
 747         * @see SpringLayout.Constraints
 748         */
 749        public void setConstraint(String edgeName, Spring s) {
 750            edgeName = edgeName.intern();
 751            if (edgeName == WEST) {
 752                setX(s);
 753            } else if (edgeName == NORTH) {
 754                setY(s);
 755            } else if (edgeName == EAST) {
 756                setEast(s);
 757            } else if (edgeName == SOUTH) {
 758                setSouth(s);
 759            } else if (edgeName == HORIZONTAL_CENTER) {
 760                setHorizontalCenter(s);
 761            } else if (edgeName == WIDTH) {
 762                setWidth(s);
 763            } else if (edgeName == HEIGHT) {
 764                setHeight(s);
 765            } else if (edgeName == VERTICAL_CENTER) {
 766                setVerticalCenter(s);
 767            } else if (edgeName == BASELINE) {
 768                setBaseline(s);
 769            }
 770        }
 771 
 772        /**
 773         * Returns the value of the specified edge, which may be
 774         * a derived value, or even <code>null</code>.
 775         * The edge must have one of the following values:
 776         * <code>SpringLayout.NORTH</code>,
 777         * <code>SpringLayout.SOUTH</code>,
 778         * <code>SpringLayout.EAST</code>,
 779         * <code>SpringLayout.WEST</code>,
 780         * <code>SpringLayout.HORIZONTAL_CENTER</code>,
 781         * <code>SpringLayout.VERTICAL_CENTER</code>,
 782         * <code>SpringLayout.BASELINE</code>,
 783         * <code>SpringLayout.WIDTH</code> or
 784         * <code>SpringLayout.HEIGHT</code>.
 785         * For any other <code>String</code> value passed as the edge,
 786         * <code>null</code> will be returned. Throws
 787         * <code>NullPointerException</code> for a <code>null</code> edge.
 788         *
 789         * @param edgeName the edge whose value
 790         *                 is to be returned
 791         *
 792         * @return the spring controlling the specified edge, may be <code>null</code>
 793         *
 794         * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
 795         *
 796         * @see #setConstraint
 797         * @see #NORTH
 798         * @see #SOUTH
 799         * @see #EAST
 800         * @see #WEST
 801         * @see #HORIZONTAL_CENTER
 802         * @see #VERTICAL_CENTER
 803         * @see #BASELINE
 804         * @see #WIDTH
 805         * @see #HEIGHT
 806         * @see SpringLayout.Constraints
 807         */
 808        public Spring getConstraint(String edgeName) {
 809            edgeName = edgeName.intern();
 810            return (edgeName == WEST)  ? getX() :
 811                    (edgeName == NORTH) ? getY() :
 812                    (edgeName == EAST)  ? getEast() :
 813                    (edgeName == SOUTH) ? getSouth() :
 814                    (edgeName == WIDTH)  ? getWidth() :
 815                    (edgeName == HEIGHT) ? getHeight() :
 816                    (edgeName == HORIZONTAL_CENTER) ? getHorizontalCenter() :
 817                    (edgeName == VERTICAL_CENTER)  ? getVerticalCenter() :
 818                    (edgeName == BASELINE) ? getBaseline() :
 819                   null;
 820        }
 821 
 822        /*pp*/ void reset() {
 823            Spring[] allSprings = {x, y, width, height, east, south,
 824                horizontalCenter, verticalCenter, baseline};
 825            for (int i = 0; i < allSprings.length; i++) {
 826                Spring s = allSprings[i];
 827                if (s != null) {
 828                    s.setValue(Spring.UNSET);
 829                }
 830            }
 831        }
 832    }
 833 
 834    private static class SpringProxy extends Spring {
 835        private String edgeName;
 836        private Component c;
 837        private SpringLayout l;
 838 
 839        public SpringProxy(String edgeName, Component c, SpringLayout l) {
 840            this.edgeName = edgeName;
 841            this.c = c;
 842            this.l = l;
 843        }
 844 
 845        private Spring getConstraint() {
 846            return l.getConstraints(c).getConstraint(edgeName);
 847        }
 848 
 849        public int getMinimumValue() {
 850            return getConstraint().getMinimumValue();
 851        }
 852 
 853        public int getPreferredValue() {
 854            return getConstraint().getPreferredValue();
 855        }
 856 
 857        public int getMaximumValue() {
 858            return getConstraint().getMaximumValue();
 859        }
 860 
 861        public int getValue() {
 862            return getConstraint().getValue();
 863        }
 864 
 865        public void setValue(int size) {
 866            getConstraint().setValue(size);
 867        }
 868 
 869        /*pp*/ boolean isCyclic(SpringLayout l) {
 870            return l.isCyclic(getConstraint());
 871        }
 872 
 873        public String toString() {
 874            return "SpringProxy for " + edgeName + " edge of " + c.getName() + ".";
 875        }
 876     }
 877 
 878     /**
 879      * Constructs a new <code>SpringLayout</code>.
 880      */
 881     public SpringLayout() {}
 882 
 883     private void resetCyclicStatuses() {
 884         cyclicSprings = new HashSet();
 885         acyclicSprings = new HashSet();
 886     }
 887 
 888     private void setParent(Container p) {
 889         resetCyclicStatuses();
 890         Constraints pc = getConstraints(p);
 891 
 892         pc.setX(Spring.constant(0));
 893         pc.setY(Spring.constant(0));
 894         // The applyDefaults() method automatically adds width and
 895         // height springs that delegate their calculations to the
 896         // getMinimumSize(), getPreferredSize() and getMaximumSize()
 897         // methods of the relevant component. In the case of the
 898         // parent this will cause an infinite loop since these
 899         // methods, in turn, delegate their calculations to the
 900         // layout manager. Check for this case and replace the
 901         // the springs that would cause this problem with a
 902         // constant springs that supply default values.
 903         Spring width = pc.getWidth();
 904         if (width instanceof Spring.WidthSpring && ((Spring.WidthSpring)width).c == p) {
 905             pc.setWidth(Spring.constant(0, 0, Integer.MAX_VALUE));
 906         }
 907         Spring height = pc.getHeight();
 908         if (height instanceof Spring.HeightSpring && ((Spring.HeightSpring)height).c == p) {
 909             pc.setHeight(Spring.constant(0, 0, Integer.MAX_VALUE));
 910         }
 911     }
 912 
 913     /*pp*/ boolean isCyclic(Spring s) {
 914         if (s == null) {
 915             return false;
 916         }
 917         if (cyclicSprings.contains(s)) {
 918             return true;
 919         }
 920         if (acyclicSprings.contains(s)) {
 921             return false;
 922         }
 923         cyclicSprings.add(s);
 924         boolean result = s.isCyclic(this);
 925         if (!result) {
 926             acyclicSprings.add(s);
 927             cyclicSprings.remove(s);
 928         }
 929         else {
 930             System.err.println(s + " is cyclic. ");
 931         }
 932         return result;
 933     }
 934 
 935     private Spring abandonCycles(Spring s) {
 936         return isCyclic(s) ? cyclicReference : s;
 937     }
 938 
 939     // LayoutManager methods.
 940 
 941     /**
 942      * Has no effect,
 943      * since this layout manager does not
 944      * use a per-component string.
 945      */
 946     public void addLayoutComponent(String name, Component c) {}
 947 
 948     /**
 949      * Removes the constraints associated with the specified component.
 950      *
 951      * @param c the component being removed from the container
 952      */
 953     public void removeLayoutComponent(Component c) {
 954         componentConstraints.remove(c);
 955     }
 956 
 957     private static Dimension addInsets(int width, int height, Container p) {
 958         Insets i = p.getInsets();
 959         return new Dimension(width + i.left + i.right, height + i.top + i.bottom);
 960     }
 961 
 962     public Dimension minimumLayoutSize(Container parent) {
 963         setParent(parent);
 964         Constraints pc = getConstraints(parent);
 965         return addInsets(abandonCycles(pc.getWidth()).getMinimumValue(),
 966                          abandonCycles(pc.getHeight()).getMinimumValue(),
 967                          parent);
 968     }
 969 
 970     public Dimension preferredLayoutSize(Container parent) {
 971         setParent(parent);
 972         Constraints pc = getConstraints(parent);
 973         return addInsets(abandonCycles(pc.getWidth()).getPreferredValue(),
 974                          abandonCycles(pc.getHeight()).getPreferredValue(),
 975                          parent);
 976     }
 977 
 978     // LayoutManager2 methods.
 979 
 980     public Dimension maximumLayoutSize(Container parent) {
 981         setParent(parent);
 982         Constraints pc = getConstraints(parent);
 983         return addInsets(abandonCycles(pc.getWidth()).getMaximumValue(),
 984                          abandonCycles(pc.getHeight()).getMaximumValue(),
 985                          parent);
 986     }
 987 
 988     /**
 989      * If <code>constraints</code> is an instance of
 990      * <code>SpringLayout.Constraints</code>,
 991      * associates the constraints with the specified component.
 992      * <p>
 993      * @param   component the component being added
 994      * @param   constraints the component's constraints
 995      *
 996      * @see SpringLayout.Constraints
 997      */
 998     public void addLayoutComponent(Component component, Object constraints) {
 999         if (constraints instanceof Constraints) {
1000             putConstraints(component, (Constraints)constraints);
1001         }
1002     }
1003 
1004     /**
1005      * Returns 0.5f (centered).
1006      */
1007     public float getLayoutAlignmentX(Container p) {
1008         return 0.5f;
1009     }
1010 
1011     /**
1012      * Returns 0.5f (centered).
1013      */
1014     public float getLayoutAlignmentY(Container p) {
1015         return 0.5f;
1016     }
1017 
1018     public void invalidateLayout(Container p) {}
1019 
1020     // End of LayoutManger2 methods
1021 
1022    /**
1023      * Links edge <code>e1</code> of component <code>c1</code> to
1024      * edge <code>e2</code> of component <code>c2</code>,
1025      * with a fixed distance between the edges. This
1026      * constraint will cause the assignment
1027      * <pre>
1028      *     value(e1, c1) = value(e2, c2) + pad</pre>
1029      * to take place during all subsequent layout operations.
1030      * <p>
1031      * @param   e1 the edge of the dependent
1032      * @param   c1 the component of the dependent
1033      * @param   pad the fixed distance between dependent and anchor
1034      * @param   e2 the edge of the anchor
1035      * @param   c2 the component of the anchor
1036      *
1037      * @see #putConstraint(String, Component, Spring, String, Component)
1038      */
1039     public void putConstraint(String e1, Component c1, int pad, String e2, Component c2) {
1040         putConstraint(e1, c1, Spring.constant(pad), e2, c2);
1041     }
1042 
1043     /**
1044      * Links edge <code>e1</code> of component <code>c1</code> to
1045      * edge <code>e2</code> of component <code>c2</code>. As edge
1046      * <code>(e2, c2)</code> changes value, edge <code>(e1, c1)</code> will
1047      * be calculated by taking the (spring) sum of <code>(e2, c2)</code>
1048      * and <code>s</code>.
1049      * Each edge must have one of the following values:
1050      * <code>SpringLayout.NORTH</code>,
1051      * <code>SpringLayout.SOUTH</code>,
1052      * <code>SpringLayout.EAST</code>,
1053      * <code>SpringLayout.WEST</code>,
1054      * <code>SpringLayout.VERTICAL_CENTER</code>,
1055      * <code>SpringLayout.HORIZONTAL_CENTER</code> or
1056      * <code>SpringLayout.BASELINE</code>.
1057      * <p>
1058      * @param   e1 the edge of the dependent
1059      * @param   c1 the component of the dependent
1060      * @param   s the spring linking dependent and anchor
1061      * @param   e2 the edge of the anchor
1062      * @param   c2 the component of the anchor
1063      *
1064      * @see #putConstraint(String, Component, int, String, Component)
1065      * @see #NORTH
1066      * @see #SOUTH
1067      * @see #EAST
1068      * @see #WEST
1069      * @see #VERTICAL_CENTER
1070      * @see #HORIZONTAL_CENTER
1071      * @see #BASELINE
1072      */
1073     public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2) {
1074         putConstraint(e1, c1, Spring.sum(s, getConstraint(e2, c2)));
1075     }
1076 
1077     private void putConstraint(String e, Component c, Spring s) {
1078         if (s != null) {
1079             getConstraints(c).setConstraint(e, s);
1080         }
1081      }
1082 
1083     private Constraints applyDefaults(Component c, Constraints constraints) {
1084         if (constraints == null) {
1085             constraints = new Constraints();
1086         }
1087         if (constraints.c == null) {
1088             constraints.c = c;
1089         }
1090         if (constraints.horizontalHistory.size() < 2) {
1091             applyDefaults(constraints, WEST, Spring.constant(0), WIDTH,
1092                           Spring.width(c), constraints.horizontalHistory);
1093         }
1094         if (constraints.verticalHistory.size() < 2) {
1095             applyDefaults(constraints, NORTH, Spring.constant(0), HEIGHT,
1096                           Spring.height(c), constraints.verticalHistory);
1097         }
1098         return constraints;
1099     }
1100 
1101     private void applyDefaults(Constraints constraints, String name1,
1102                                Spring spring1, String name2, Spring spring2,
1103                                List<String> history) {
1104         if (history.size() == 0) {
1105             constraints.setConstraint(name1, spring1);
1106             constraints.setConstraint(name2, spring2);
1107         } else {
1108             // At this point there must be exactly one constraint defined already.
1109             // Check width/height first.
1110             if (constraints.getConstraint(name2) == null) {
1111                 constraints.setConstraint(name2, spring2);
1112             } else {
1113                 // If width/height is already defined, install a default for x/y.
1114                 constraints.setConstraint(name1, spring1);
1115             }
1116             // Either way, leave the user's constraint topmost on the stack.
1117             Collections.rotate(history, 1);
1118         }
1119     }
1120 
1121     private void putConstraints(Component component, Constraints constraints) {
1122         componentConstraints.put(component, applyDefaults(component, constraints));
1123     }
1124 
1125     /**
1126      * Returns the constraints for the specified component.
1127      * Note that,
1128      * unlike the <code>GridBagLayout</code>
1129      * <code>getConstraints</code> method,
1130      * this method does not clone constraints.
1131      * If no constraints
1132      * have been associated with this component,
1133      * this method
1134      * returns a default constraints object positioned at
1135      * 0,0 relative to the parent's Insets and its width/height
1136      * constrained to the minimum, maximum, and preferred sizes of the
1137      * component. The size characteristics
1138      * are not frozen at the time this method is called;
1139      * instead this method returns a constraints object
1140      * whose characteristics track the characteristics
1141      * of the component as they change.
1142      *
1143      * @param       c the component whose constraints will be returned
1144      *
1145      * @return      the constraints for the specified component
1146      */
1147     public Constraints getConstraints(Component c) {
1148        Constraints result = (Constraints)componentConstraints.get(c);
1149        if (result == null) {
1150            if (c instanceof javax.swing.JComponent) {
1151                 Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class);
1152                 if (cp instanceof Constraints) {
1153                     return applyDefaults(c, (Constraints)cp);
1154                 }
1155             }
1156             result = new Constraints();
1157             putConstraints(c, result);
1158        }
1159        return result;
1160     }
1161 
1162     /**
1163      * Returns the spring controlling the distance between
1164      * the specified edge of
1165      * the component and the top or left edge of its parent. This
1166      * method, instead of returning the current binding for the
1167      * edge, returns a proxy that tracks the characteristics
1168      * of the edge even if the edge is subsequently rebound.
1169      * Proxies are intended to be used in builder envonments
1170      * where it is useful to allow the user to define the
1171      * constraints for a layout in any order. Proxies do, however,
1172      * provide the means to create cyclic dependencies amongst
1173      * the constraints of a layout. Such cycles are detected
1174      * internally by <code>SpringLayout</code> so that
1175      * the layout operation always terminates.
1176      *
1177      * @param edgeName must be one of
1178      * <code>SpringLayout.NORTH</code>,
1179      * <code>SpringLayout.SOUTH</code>,
1180      * <code>SpringLayout.EAST</code>,
1181      * <code>SpringLayout.WEST</code>,
1182      * <code>SpringLayout.VERTICAL_CENTER</code>,
1183      * <code>SpringLayout.HORIZONTAL_CENTER</code> or
1184      * <code>SpringLayout.BASELINE</code>
1185      * @param c the component whose edge spring is desired
1186      *
1187      * @return a proxy for the spring controlling the distance between the
1188      *         specified edge and the top or left edge of its parent
1189      *
1190      * @see #NORTH
1191      * @see #SOUTH
1192      * @see #EAST
1193      * @see #WEST
1194      * @see #VERTICAL_CENTER
1195      * @see #HORIZONTAL_CENTER
1196      * @see #BASELINE
1197      */
1198     public Spring getConstraint(String edgeName, Component c) {
1199         // The interning here is unnecessary; it was added for efficiency.
1200         edgeName = edgeName.intern();
1201         return new SpringProxy(edgeName, c, this);
1202     }
1203 
1204     public void layoutContainer(Container parent) {
1205         setParent(parent);
1206 
1207         int n = parent.getComponentCount();
1208         getConstraints(parent).reset();
1209         for (int i = 0 ; i < n ; i++) {
1210             getConstraints(parent.getComponent(i)).reset();
1211         }
1212 
1213         Insets insets = parent.getInsets();
1214         Constraints pc = getConstraints(parent);
1215         abandonCycles(pc.getX()).setValue(0);
1216         abandonCycles(pc.getY()).setValue(0);
1217         abandonCycles(pc.getWidth()).setValue(parent.getWidth() -
1218                                               insets.left - insets.right);
1219         abandonCycles(pc.getHeight()).setValue(parent.getHeight() -
1220                                                insets.top - insets.bottom);
1221 
1222         for (int i = 0 ; i < n ; i++) {
1223             Component c = parent.getComponent(i);
1224             Constraints cc = getConstraints(c);
1225             int x = abandonCycles(cc.getX()).getValue();
1226             int y = abandonCycles(cc.getY()).getValue();
1227             int width = abandonCycles(cc.getWidth()).getValue();
1228             int height = abandonCycles(cc.getHeight()).getValue();
1229             c.setBounds(insets.left + x, insets.top + y, width, height);
1230         }
1231     }
1232 }