1 /*
   2  * Copyright (c) 2001, 2014, 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="https://docs.oracle.com/javase/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  * <h2>
 132  * SpringLayout vs. Other Layout Managers
 133  * </h2>
 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&trade;
 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 @SuppressWarnings("serial") // Same-version serialization only
 188 public class SpringLayout implements LayoutManager2 {
 189     private Map<Component, Constraints> componentConstraints = new HashMap<Component, Constraints>();
 190 
 191     private Spring cyclicReference = Spring.constant(Spring.UNSET);
 192     private Set<Spring> cyclicSprings;
 193     private Set<Spring> acyclicSprings;
 194 
 195 
 196     /**
 197      * Specifies the top edge of a component's bounding rectangle.
 198      */
 199     public static final String NORTH  = "North";
 200 
 201     /**
 202      * Specifies the bottom edge of a component's bounding rectangle.
 203      */
 204     public static final String SOUTH  = "South";
 205 
 206     /**
 207      * Specifies the right edge of a component's bounding rectangle.
 208      */
 209     public static final String EAST   = "East";
 210 
 211     /**
 212      * Specifies the left edge of a component's bounding rectangle.
 213      */
 214     public static final String WEST   = "West";
 215 
 216     /**
 217      * Specifies the horizontal center of a component's bounding rectangle.
 218      *
 219      * @since 1.6
 220      */
 221     public static final String HORIZONTAL_CENTER   = "HorizontalCenter";
 222 
 223     /**
 224      * Specifies the vertical center of a component's bounding rectangle.
 225      *
 226      * @since 1.6
 227      */
 228     public static final String VERTICAL_CENTER   = "VerticalCenter";
 229 
 230     /**
 231      * Specifies the baseline of a component.
 232      *
 233      * @since 1.6
 234      */
 235     public static final String BASELINE   = "Baseline";
 236 
 237     /**
 238      * Specifies the width of a component's bounding rectangle.
 239      *
 240      * @since 1.6
 241      */
 242     public static final String WIDTH = "Width";
 243 
 244     /**
 245      * Specifies the height of a component's bounding rectangle.
 246      *
 247      * @since 1.6
 248      */
 249     public static final String HEIGHT = "Height";
 250 
 251     private static String[] ALL_HORIZONTAL = {WEST, WIDTH, EAST, HORIZONTAL_CENTER};
 252 
 253     private static String[] ALL_VERTICAL = {NORTH, HEIGHT, SOUTH, VERTICAL_CENTER, BASELINE};
 254 
 255     /**
 256      * A <code>Constraints</code> object holds the
 257      * constraints that govern the way a component's size and position
 258      * change in a container controlled by a <code>SpringLayout</code>.
 259      * A <code>Constraints</code> object is
 260      * like a <code>Rectangle</code>, in that it
 261      * has <code>x</code>, <code>y</code>,
 262      * <code>width</code>, and <code>height</code> properties.
 263      * In the <code>Constraints</code> object, however,
 264      * these properties have
 265      * <code>Spring</code> values instead of integers.
 266      * In addition,
 267      * a <code>Constraints</code> object
 268      * can be manipulated as four edges
 269      * -- north, south, east, and west --
 270      * using the <code>constraint</code> property.
 271      *
 272      * <p>
 273      * The following formulas are always true
 274      * for a <code>Constraints</code> object (here WEST and <code>x</code> are synonyms, as are and NORTH and <code>y</code>):
 275      *
 276      * <pre>
 277      *               EAST = WEST + WIDTH
 278      *              SOUTH = NORTH + HEIGHT
 279      *  HORIZONTAL_CENTER = WEST + WIDTH/2
 280      *    VERTICAL_CENTER = NORTH + HEIGHT/2
 281      *  ABSOLUTE_BASELINE = NORTH + RELATIVE_BASELINE*
 282      * </pre>
 283      * <p>
 284      * For example, if you have specified the WIDTH and WEST (X) location
 285      * the EAST is calculated as WEST + WIDTH.  If you instead specified
 286      * the WIDTH and EAST locations the WEST (X) location is then calculated
 287      * as EAST - WIDTH.
 288      * <p>
 289      * [RELATIVE_BASELINE is a private constraint that is set automatically when
 290      * the SpringLayout.Constraints(Component) constructor is called or when
 291      * a constraints object is registered with a SpringLayout object.]
 292      * <p>
 293      * <b>Note</b>: In this document,
 294      * operators represent methods
 295      * in the <code>Spring</code> class.
 296      * For example, "a + b" is equal to
 297      * <code>Spring.sum(a, b)</code>,
 298      * and "a - b" is equal to
 299      * <code>Spring.sum(a, Spring.minus(b))</code>.
 300      * See the
 301      * {@link Spring Spring API documentation}
 302      * for further details
 303      * of spring arithmetic.
 304      *
 305      * <p>
 306      *
 307      * Because a <code>Constraints</code> object's properties --
 308      * representing its edges, size, and location -- can all be set
 309      * independently and yet are interrelated,
 310      * a <code>Constraints</code> object can become <em>over-constrained</em>.
 311      * For example, if the <code>WEST</code>, <code>WIDTH</code> and
 312      * <code>EAST</code> edges are all set, steps must be taken to ensure that
 313      * the first of the formulas above holds.  To do this, the
 314      * <code>Constraints</code>
 315      * object throws away the <em>least recently set</em>
 316      * constraint so as to make the formulas hold.
 317      * @since 1.4
 318      */
 319     public static class Constraints {
 320        private Spring x;
 321        private Spring y;
 322        private Spring width;
 323        private Spring height;
 324        private Spring east;
 325        private Spring south;
 326         private Spring horizontalCenter;
 327         private Spring verticalCenter;
 328         private Spring baseline;
 329 
 330         private List<String> horizontalHistory = new ArrayList<String>(2);
 331         private List<String> verticalHistory = new ArrayList<String>(2);
 332 
 333         // Used for baseline calculations
 334         private Component c;
 335 
 336        /**
 337         * Creates an empty <code>Constraints</code> object.
 338         */
 339        public Constraints() {
 340        }
 341 
 342        /**
 343         * Creates a <code>Constraints</code> object with the
 344         * specified values for its
 345         * <code>x</code> and <code>y</code> properties.
 346         * The <code>height</code> and <code>width</code> springs
 347         * have <code>null</code> values.
 348         *
 349         * @param x  the spring controlling the component's <em>x</em> value
 350         * @param y  the spring controlling the component's <em>y</em> value
 351         */
 352        public Constraints(Spring x, Spring y) {
 353            setX(x);
 354            setY(y);
 355        }
 356 
 357        /**
 358         * Creates a <code>Constraints</code> object with the
 359         * specified values for its
 360         * <code>x</code>, <code>y</code>, <code>width</code>,
 361         * and <code>height</code> properties.
 362         * Note: If the <code>SpringLayout</code> class
 363         * encounters <code>null</code> values in the
 364         * <code>Constraints</code> object of a given component,
 365         * it replaces them with suitable defaults.
 366         *
 367         * @param x  the spring value for the <code>x</code> property
 368         * @param y  the spring value for the <code>y</code> property
 369         * @param width  the spring value for the <code>width</code> property
 370         * @param height  the spring value for the <code>height</code> property
 371         */
 372        public Constraints(Spring x, Spring y, Spring width, Spring height) {
 373            setX(x);
 374            setY(y);
 375            setWidth(width);
 376            setHeight(height);
 377        }
 378 
 379         /**
 380          * Creates a <code>Constraints</code> object with
 381          * suitable <code>x</code>, <code>y</code>, <code>width</code> and
 382          * <code>height</code> springs for component, <code>c</code>.
 383          * The <code>x</code> and <code>y</code> springs are constant
 384          * springs  initialised with the component's location at
 385          * the time this method is called. The <code>width</code> and
 386          * <code>height</code> springs are special springs, created by
 387          * the <code>Spring.width()</code> and <code>Spring.height()</code>
 388          * methods, which track the size characteristics of the component
 389          * when they change.
 390          *
 391          * @param c  the component whose characteristics will be reflected by this Constraints object
 392          * @throws NullPointerException if <code>c</code> is null.
 393          * @since 1.5
 394          */
 395         public Constraints(Component c) {
 396             this.c = c;
 397             setX(Spring.constant(c.getX()));
 398             setY(Spring.constant(c.getY()));
 399             setWidth(Spring.width(c));
 400             setHeight(Spring.height(c));
 401         }
 402 
 403         private void pushConstraint(String name, Spring value, boolean horizontal) {
 404             boolean valid = true;
 405             List<String> history = horizontal ? horizontalHistory :
 406                                                 verticalHistory;
 407             if (history.contains(name)) {
 408                 history.remove(name);
 409                 valid = false;
 410             } else if (history.size() == 2 && value != null) {
 411                 history.remove(0);
 412                 valid = false;
 413             }
 414             if (value != null) {
 415                 history.add(name);
 416             }
 417             if (!valid) {
 418                 String[] all = horizontal ? ALL_HORIZONTAL : ALL_VERTICAL;
 419                 for (String s : all) {
 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         * <p>
 732         * <b>Note:</b> This method can affect {@code x} and {@code y} values
 733         * previously set for this {@code Constraints}.
 734         *
 735         * @param edgeName the edge to be set
 736         * @param s the spring controlling the specified edge
 737         *
 738         * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
 739         *
 740         * @see #getConstraint
 741         * @see #NORTH
 742         * @see #SOUTH
 743         * @see #EAST
 744         * @see #WEST
 745         * @see #HORIZONTAL_CENTER
 746         * @see #VERTICAL_CENTER
 747         * @see #BASELINE
 748         * @see #WIDTH
 749         * @see #HEIGHT
 750         * @see SpringLayout.Constraints
 751         */
 752        public void setConstraint(String edgeName, Spring s) {
 753            edgeName = edgeName.intern();
 754            if (edgeName == WEST) {
 755                setX(s);
 756            } else if (edgeName == NORTH) {
 757                setY(s);
 758            } else if (edgeName == EAST) {
 759                setEast(s);
 760            } else if (edgeName == SOUTH) {
 761                setSouth(s);
 762            } else if (edgeName == HORIZONTAL_CENTER) {
 763                setHorizontalCenter(s);
 764            } else if (edgeName == WIDTH) {
 765                setWidth(s);
 766            } else if (edgeName == HEIGHT) {
 767                setHeight(s);
 768            } else if (edgeName == VERTICAL_CENTER) {
 769                setVerticalCenter(s);
 770            } else if (edgeName == BASELINE) {
 771                setBaseline(s);
 772            }
 773        }
 774 
 775        /**
 776         * Returns the value of the specified edge, which may be
 777         * a derived value, or even <code>null</code>.
 778         * The edge must have one of the following values:
 779         * <code>SpringLayout.NORTH</code>,
 780         * <code>SpringLayout.SOUTH</code>,
 781         * <code>SpringLayout.EAST</code>,
 782         * <code>SpringLayout.WEST</code>,
 783         * <code>SpringLayout.HORIZONTAL_CENTER</code>,
 784         * <code>SpringLayout.VERTICAL_CENTER</code>,
 785         * <code>SpringLayout.BASELINE</code>,
 786         * <code>SpringLayout.WIDTH</code> or
 787         * <code>SpringLayout.HEIGHT</code>.
 788         * For any other <code>String</code> value passed as the edge,
 789         * <code>null</code> will be returned. Throws
 790         * <code>NullPointerException</code> for a <code>null</code> edge.
 791         *
 792         * @param edgeName the edge whose value
 793         *                 is to be returned
 794         *
 795         * @return the spring controlling the specified edge, may be <code>null</code>
 796         *
 797         * @throws NullPointerException if <code>edgeName</code> is <code>null</code>
 798         *
 799         * @see #setConstraint
 800         * @see #NORTH
 801         * @see #SOUTH
 802         * @see #EAST
 803         * @see #WEST
 804         * @see #HORIZONTAL_CENTER
 805         * @see #VERTICAL_CENTER
 806         * @see #BASELINE
 807         * @see #WIDTH
 808         * @see #HEIGHT
 809         * @see SpringLayout.Constraints
 810         */
 811        public Spring getConstraint(String edgeName) {
 812            edgeName = edgeName.intern();
 813            return (edgeName == WEST)  ? getX() :
 814                    (edgeName == NORTH) ? getY() :
 815                    (edgeName == EAST)  ? getEast() :
 816                    (edgeName == SOUTH) ? getSouth() :
 817                    (edgeName == WIDTH)  ? getWidth() :
 818                    (edgeName == HEIGHT) ? getHeight() :
 819                    (edgeName == HORIZONTAL_CENTER) ? getHorizontalCenter() :
 820                    (edgeName == VERTICAL_CENTER)  ? getVerticalCenter() :
 821                    (edgeName == BASELINE) ? getBaseline() :
 822                   null;
 823        }
 824 
 825        /*pp*/ void reset() {
 826            Spring[] allSprings = {x, y, width, height, east, south,
 827                horizontalCenter, verticalCenter, baseline};
 828            for (Spring s : allSprings) {
 829                if (s != null) {
 830                    s.setValue(Spring.UNSET);
 831                }
 832            }
 833        }
 834    }
 835 
 836    private static class SpringProxy extends Spring {
 837        private String edgeName;
 838        private Component c;
 839        private SpringLayout l;
 840 
 841        public SpringProxy(String edgeName, Component c, SpringLayout l) {
 842            this.edgeName = edgeName;
 843            this.c = c;
 844            this.l = l;
 845        }
 846 
 847        private Spring getConstraint() {
 848            return l.getConstraints(c).getConstraint(edgeName);
 849        }
 850 
 851        public int getMinimumValue() {
 852            return getConstraint().getMinimumValue();
 853        }
 854 
 855        public int getPreferredValue() {
 856            return getConstraint().getPreferredValue();
 857        }
 858 
 859        public int getMaximumValue() {
 860            return getConstraint().getMaximumValue();
 861        }
 862 
 863        public int getValue() {
 864            return getConstraint().getValue();
 865        }
 866 
 867        public void setValue(int size) {
 868            getConstraint().setValue(size);
 869        }
 870 
 871        /*pp*/ boolean isCyclic(SpringLayout l) {
 872            return l.isCyclic(getConstraint());
 873        }
 874 
 875        public String toString() {
 876            return "SpringProxy for " + edgeName + " edge of " + c.getName() + ".";
 877        }
 878     }
 879 
 880     /**
 881      * Constructs a new <code>SpringLayout</code>.
 882      */
 883     public SpringLayout() {}
 884 
 885     private void resetCyclicStatuses() {
 886         cyclicSprings = new HashSet<Spring>();
 887         acyclicSprings = new HashSet<Spring>();
 888     }
 889 
 890     private void setParent(Container p) {
 891         resetCyclicStatuses();
 892         Constraints pc = getConstraints(p);
 893 
 894         pc.setX(Spring.constant(0));
 895         pc.setY(Spring.constant(0));
 896         // The applyDefaults() method automatically adds width and
 897         // height springs that delegate their calculations to the
 898         // getMinimumSize(), getPreferredSize() and getMaximumSize()
 899         // methods of the relevant component. In the case of the
 900         // parent this will cause an infinite loop since these
 901         // methods, in turn, delegate their calculations to the
 902         // layout manager. Check for this case and replace the
 903         // the springs that would cause this problem with a
 904         // constant springs that supply default values.
 905         Spring width = pc.getWidth();
 906         if (width instanceof Spring.WidthSpring && ((Spring.WidthSpring)width).c == p) {
 907             pc.setWidth(Spring.constant(0, 0, Integer.MAX_VALUE));
 908         }
 909         Spring height = pc.getHeight();
 910         if (height instanceof Spring.HeightSpring && ((Spring.HeightSpring)height).c == p) {
 911             pc.setHeight(Spring.constant(0, 0, Integer.MAX_VALUE));
 912         }
 913     }
 914 
 915     /*pp*/ boolean isCyclic(Spring s) {
 916         if (s == null) {
 917             return false;
 918         }
 919         if (cyclicSprings.contains(s)) {
 920             return true;
 921         }
 922         if (acyclicSprings.contains(s)) {
 923             return false;
 924         }
 925         cyclicSprings.add(s);
 926         boolean result = s.isCyclic(this);
 927         if (!result) {
 928             acyclicSprings.add(s);
 929             cyclicSprings.remove(s);
 930         }
 931         else {
 932             System.err.println(s + " is cyclic. ");
 933         }
 934         return result;
 935     }
 936 
 937     private Spring abandonCycles(Spring s) {
 938         return isCyclic(s) ? cyclicReference : s;
 939     }
 940 
 941     // LayoutManager methods.
 942 
 943     /**
 944      * Has no effect,
 945      * since this layout manager does not
 946      * use a per-component string.
 947      */
 948     public void addLayoutComponent(String name, Component c) {}
 949 
 950     /**
 951      * Removes the constraints associated with the specified component.
 952      *
 953      * @param c the component being removed from the container
 954      */
 955     public void removeLayoutComponent(Component c) {
 956         componentConstraints.remove(c);
 957     }
 958 
 959     private static Dimension addInsets(int width, int height, Container p) {
 960         Insets i = p.getInsets();
 961         return new Dimension(width + i.left + i.right, height + i.top + i.bottom);
 962     }
 963 
 964     public Dimension minimumLayoutSize(Container parent) {
 965         setParent(parent);
 966         Constraints pc = getConstraints(parent);
 967         return addInsets(abandonCycles(pc.getWidth()).getMinimumValue(),
 968                          abandonCycles(pc.getHeight()).getMinimumValue(),
 969                          parent);
 970     }
 971 
 972     public Dimension preferredLayoutSize(Container parent) {
 973         setParent(parent);
 974         Constraints pc = getConstraints(parent);
 975         return addInsets(abandonCycles(pc.getWidth()).getPreferredValue(),
 976                          abandonCycles(pc.getHeight()).getPreferredValue(),
 977                          parent);
 978     }
 979 
 980     // LayoutManager2 methods.
 981 
 982     public Dimension maximumLayoutSize(Container parent) {
 983         setParent(parent);
 984         Constraints pc = getConstraints(parent);
 985         return addInsets(abandonCycles(pc.getWidth()).getMaximumValue(),
 986                          abandonCycles(pc.getHeight()).getMaximumValue(),
 987                          parent);
 988     }
 989 
 990     /**
 991      * If <code>constraints</code> is an instance of
 992      * <code>SpringLayout.Constraints</code>,
 993      * associates the constraints with the specified component.
 994      *
 995      * @param   component the component being added
 996      * @param   constraints the component's constraints
 997      *
 998      * @see SpringLayout.Constraints
 999      */
1000     public void addLayoutComponent(Component component, Object constraints) {
1001         if (constraints instanceof Constraints) {
1002             putConstraints(component, (Constraints)constraints);
1003         }
1004     }
1005 
1006     /**
1007      * Returns 0.5f (centered).
1008      */
1009     public float getLayoutAlignmentX(Container p) {
1010         return 0.5f;
1011     }
1012 
1013     /**
1014      * Returns 0.5f (centered).
1015      */
1016     public float getLayoutAlignmentY(Container p) {
1017         return 0.5f;
1018     }
1019 
1020     public void invalidateLayout(Container p) {}
1021 
1022     // End of LayoutManger2 methods
1023 
1024    /**
1025      * Links edge <code>e1</code> of component <code>c1</code> to
1026      * edge <code>e2</code> of component <code>c2</code>,
1027      * with a fixed distance between the edges. This
1028      * constraint will cause the assignment
1029      * <pre>
1030      *     value(e1, c1) = value(e2, c2) + pad</pre>
1031      * to take place during all subsequent layout operations.
1032      *
1033      * @param   e1 the edge of the dependent
1034      * @param   c1 the component of the dependent
1035      * @param   pad the fixed distance between dependent and anchor
1036      * @param   e2 the edge of the anchor
1037      * @param   c2 the component of the anchor
1038      *
1039      * @see #putConstraint(String, Component, Spring, String, Component)
1040      */
1041     public void putConstraint(String e1, Component c1, int pad, String e2, Component c2) {
1042         putConstraint(e1, c1, Spring.constant(pad), e2, c2);
1043     }
1044 
1045     /**
1046      * Links edge <code>e1</code> of component <code>c1</code> to
1047      * edge <code>e2</code> of component <code>c2</code>. As edge
1048      * <code>(e2, c2)</code> changes value, edge <code>(e1, c1)</code> will
1049      * be calculated by taking the (spring) sum of <code>(e2, c2)</code>
1050      * and <code>s</code>.
1051      * Each edge must have one of the following values:
1052      * <code>SpringLayout.NORTH</code>,
1053      * <code>SpringLayout.SOUTH</code>,
1054      * <code>SpringLayout.EAST</code>,
1055      * <code>SpringLayout.WEST</code>,
1056      * <code>SpringLayout.VERTICAL_CENTER</code>,
1057      * <code>SpringLayout.HORIZONTAL_CENTER</code> or
1058      * <code>SpringLayout.BASELINE</code>.
1059      *
1060      * @param   e1 the edge of the dependent
1061      * @param   c1 the component of the dependent
1062      * @param   s the spring linking dependent and anchor
1063      * @param   e2 the edge of the anchor
1064      * @param   c2 the component of the anchor
1065      *
1066      * @see #putConstraint(String, Component, int, String, Component)
1067      * @see #NORTH
1068      * @see #SOUTH
1069      * @see #EAST
1070      * @see #WEST
1071      * @see #VERTICAL_CENTER
1072      * @see #HORIZONTAL_CENTER
1073      * @see #BASELINE
1074      */
1075     public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2) {
1076         putConstraint(e1, c1, Spring.sum(s, getConstraint(e2, c2)));
1077     }
1078 
1079     private void putConstraint(String e, Component c, Spring s) {
1080         if (s != null) {
1081             getConstraints(c).setConstraint(e, s);
1082         }
1083      }
1084 
1085     private Constraints applyDefaults(Component c, Constraints constraints) {
1086         if (constraints == null) {
1087             constraints = new Constraints();
1088         }
1089         if (constraints.c == null) {
1090             constraints.c = c;
1091         }
1092         if (constraints.horizontalHistory.size() < 2) {
1093             applyDefaults(constraints, WEST, Spring.constant(0), WIDTH,
1094                           Spring.width(c), constraints.horizontalHistory);
1095         }
1096         if (constraints.verticalHistory.size() < 2) {
1097             applyDefaults(constraints, NORTH, Spring.constant(0), HEIGHT,
1098                           Spring.height(c), constraints.verticalHistory);
1099         }
1100         return constraints;
1101     }
1102 
1103     private void applyDefaults(Constraints constraints, String name1,
1104                                Spring spring1, String name2, Spring spring2,
1105                                List<String> history) {
1106         if (history.size() == 0) {
1107             constraints.setConstraint(name1, spring1);
1108             constraints.setConstraint(name2, spring2);
1109         } else {
1110             // At this point there must be exactly one constraint defined already.
1111             // Check width/height first.
1112             if (constraints.getConstraint(name2) == null) {
1113                 constraints.setConstraint(name2, spring2);
1114             } else {
1115                 // If width/height is already defined, install a default for x/y.
1116                 constraints.setConstraint(name1, spring1);
1117             }
1118             // Either way, leave the user's constraint topmost on the stack.
1119             Collections.rotate(history, 1);
1120         }
1121     }
1122 
1123     private void putConstraints(Component component, Constraints constraints) {
1124         componentConstraints.put(component, applyDefaults(component, constraints));
1125     }
1126 
1127     /**
1128      * Returns the constraints for the specified component.
1129      * Note that,
1130      * unlike the <code>GridBagLayout</code>
1131      * <code>getConstraints</code> method,
1132      * this method does not clone constraints.
1133      * If no constraints
1134      * have been associated with this component,
1135      * this method
1136      * returns a default constraints object positioned at
1137      * 0,0 relative to the parent's Insets and its width/height
1138      * constrained to the minimum, maximum, and preferred sizes of the
1139      * component. The size characteristics
1140      * are not frozen at the time this method is called;
1141      * instead this method returns a constraints object
1142      * whose characteristics track the characteristics
1143      * of the component as they change.
1144      *
1145      * @param       c the component whose constraints will be returned
1146      *
1147      * @return      the constraints for the specified component
1148      */
1149     public Constraints getConstraints(Component c) {
1150        Constraints result = componentConstraints.get(c);
1151        if (result == null) {
1152            if (c instanceof javax.swing.JComponent) {
1153                 Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class);
1154                 if (cp instanceof Constraints) {
1155                     return applyDefaults(c, (Constraints)cp);
1156                 }
1157             }
1158             result = new Constraints();
1159             putConstraints(c, result);
1160        }
1161        return result;
1162     }
1163 
1164     /**
1165      * Returns the spring controlling the distance between
1166      * the specified edge of
1167      * the component and the top or left edge of its parent. This
1168      * method, instead of returning the current binding for the
1169      * edge, returns a proxy that tracks the characteristics
1170      * of the edge even if the edge is subsequently rebound.
1171      * Proxies are intended to be used in builder environments
1172      * where it is useful to allow the user to define the
1173      * constraints for a layout in any order. Proxies do, however,
1174      * provide the means to create cyclic dependencies amongst
1175      * the constraints of a layout. Such cycles are detected
1176      * internally by <code>SpringLayout</code> so that
1177      * the layout operation always terminates.
1178      *
1179      * @param edgeName must be one of
1180      * <code>SpringLayout.NORTH</code>,
1181      * <code>SpringLayout.SOUTH</code>,
1182      * <code>SpringLayout.EAST</code>,
1183      * <code>SpringLayout.WEST</code>,
1184      * <code>SpringLayout.VERTICAL_CENTER</code>,
1185      * <code>SpringLayout.HORIZONTAL_CENTER</code> or
1186      * <code>SpringLayout.BASELINE</code>
1187      * @param c the component whose edge spring is desired
1188      *
1189      * @return a proxy for the spring controlling the distance between the
1190      *         specified edge and the top or left edge of its parent
1191      *
1192      * @see #NORTH
1193      * @see #SOUTH
1194      * @see #EAST
1195      * @see #WEST
1196      * @see #VERTICAL_CENTER
1197      * @see #HORIZONTAL_CENTER
1198      * @see #BASELINE
1199      */
1200     public Spring getConstraint(String edgeName, Component c) {
1201         // The interning here is unnecessary; it was added for efficiency.
1202         edgeName = edgeName.intern();
1203         return new SpringProxy(edgeName, c, this);
1204     }
1205 
1206     public void layoutContainer(Container parent) {
1207         setParent(parent);
1208 
1209         int n = parent.getComponentCount();
1210         getConstraints(parent).reset();
1211         for (int i = 0 ; i < n ; i++) {
1212             getConstraints(parent.getComponent(i)).reset();
1213         }
1214 
1215         Insets insets = parent.getInsets();
1216         Constraints pc = getConstraints(parent);
1217         abandonCycles(pc.getX()).setValue(0);
1218         abandonCycles(pc.getY()).setValue(0);
1219         abandonCycles(pc.getWidth()).setValue(parent.getWidth() -
1220                                               insets.left - insets.right);
1221         abandonCycles(pc.getHeight()).setValue(parent.getHeight() -
1222                                                insets.top - insets.bottom);
1223 
1224         for (int i = 0 ; i < n ; i++) {
1225             Component c = parent.getComponent(i);
1226             Constraints cc = getConstraints(c);
1227             int x = abandonCycles(cc.getX()).getValue();
1228             int y = abandonCycles(cc.getY()).getValue();
1229             int width = abandonCycles(cc.getWidth()).getValue();
1230             int height = abandonCycles(cc.getHeight()).getValue();
1231             c.setBounds(insets.left + x, insets.top + y, width, height);
1232         }
1233     }
1234 }