1. /*
  2. * @(#)Spring.java 1.9 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.Component;
  9. import java.util.*;
  10. /**
  11. * An instance of the <code>Spring</code> class holds three properties that
  12. * characterize its behavior: the <em>minimum</em>, <em>preferred</em>, and
  13. * <em>maximum</em> values. Each of these properties may be involved in
  14. * defining its fourth, <em>value</em>, property based on a series of rules.
  15. * <p>
  16. * An instance of the <code>Spring</code> class can be visualized as a
  17. * mechanical spring that provides a corrective force as the spring is compressed
  18. * or stretched away from its preferred value. This force is modelled
  19. * as linear function of the distance from the preferred value, but with
  20. * two different constants -- one for the compressional force and one for the
  21. * tensional one. Those constants are specified by the minimum and maximum
  22. * values of the spring such that a spring at its minimum value produces an
  23. * equal and opposite force to that which is created when it is at its
  24. * maximum value. The difference between the <em>preferred</em> and
  25. * <em>minimum</em> values, therefore, represents the ease with which the
  26. * spring can be compressed and the difference between its <em>maximum</em>
  27. * and <em>preferred</em> values, indicates the ease with which the
  28. * <code>Spring</code> can be extended.
  29. * See the {@link #sum} method for details.
  30. *
  31. * <p>
  32. * By defining simple arithmetic operations on <code>Spring</code>s,
  33. * the behavior of a collection of <code>Spring</code>s
  34. * can be reduced to that of an ordinary (non-compound) <code>Spring</code>. We define
  35. * the "+", "-", <em>max</em>, and <em>min</em> operators on
  36. * <code>Spring</code>s so that, in each case, the result is a <code>Spring</code>
  37. * whose characteristics bear a useful mathematical relationship to its constituent
  38. * springs.
  39. *
  40. * <p>
  41. * A <code>Spring</code> can be treated as a pair of intervals
  42. * with a single common point: the preferred value.
  43. * The following rules define some of the
  44. * arithmetic operators that can be applied to intervals
  45. * (<code>[a, b]</code> refers to the interval
  46. * from <code>a</code>
  47. * to <code>b</code>,
  48. * where <code>a <= b</code>).
  49. * <p>
  50. * <pre>
  51. * [a1, b1] + [a2, b2] = [a1 + a2, b1 + b2]
  52. *
  53. * -[a, b] = [-b, -a]
  54. *
  55. * max([a1, b1], [a2, b2]) = [max(a1, a2), max(b1, b2)]
  56. * </pre>
  57. * <p>
  58. *
  59. * If we denote <code>Spring</code>s as <code>[a, b, c]</code>,
  60. * where <code>a <= b <= c</code>, we can define the same
  61. * arithmetic operators on <code>Spring</code>s:
  62. * <p>
  63. * <pre>
  64. * [a1, b1, c1] + [a2, b2, c2] = [a1 + a2, b1 + b2, c1 + c2]
  65. *
  66. * -[a, b, c] = [-c, -b, -a]
  67. *
  68. * max([a1, b1, c1], [a2, b2, c2]) = [max(a1, a2), max(b1, b2), max(c1, c2)]
  69. * </pre>
  70. * <p>
  71. * With both intervals and <code>Spring</code>s we can define "-" and <em>min</em>
  72. * in terms of negation:
  73. * <p>
  74. * <pre>
  75. * X - Y = X + (-Y)
  76. *
  77. * min(X, Y) = -max(-X, -Y)
  78. * </pre>
  79. * <p>
  80. * For the static methods in this class that embody the arithmetic
  81. * operators, we do not actually perform the operation in question as
  82. * that would snapshot the values of the properties of the method's arguments
  83. * at the time the static method is called. Instead, the static methods
  84. * create a new <code>Spring</code> instance containing references to
  85. * the method's arguments so that the characteristics of the new spring track the
  86. * potentially changing characteristics of the springs from which it
  87. * was made. This is a little like the idea of a <em>lazy value</em>
  88. * in a functional language.
  89. * <p>
  90. * If you are implementing a <code>SpringLayout</code> you
  91. * can find further information and examples in
  92. * <a
  93. href="http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html">How to Use SpringLayout</a>,
  94. * a section in <em>The Java Tutorial.</em>
  95. * <p>
  96. * <strong>Warning:</strong>
  97. * Serialized objects of this class will not be compatible with
  98. * future Swing releases. The current serialization support is
  99. * appropriate for short term storage or RMI between applications running
  100. * the same version of Swing. As of 1.4, support for long term storage
  101. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  102. * has been added to the <code>java.beans</code> package.
  103. * Please see {@link java.beans.XMLEncoder}.
  104. *
  105. * @see SpringLayout
  106. * @see SpringLayout.Constraints
  107. *
  108. * @version 1.9 12/19/03
  109. * @author Philip Milne
  110. * @since 1.4
  111. */
  112. public abstract class Spring {
  113. /**
  114. * An integer value signifying that a property value has not yet been calculated.
  115. */
  116. public static final int UNSET = Integer.MIN_VALUE;
  117. /**
  118. * Used by factory methods to create a <code>Spring</code>.
  119. *
  120. * @see #constant(int)
  121. * @see #constant(int, int, int)
  122. * @see #max
  123. * @see #minus
  124. * @see #sum
  125. * @see SpringLayout.Constraints
  126. */
  127. protected Spring() {}
  128. /**
  129. * Returns the <em>minimum</em> value of this <code>Spring</code>.
  130. *
  131. * @return the <code>minimumValue</code> property of this <code>Spring</code>
  132. */
  133. public abstract int getMinimumValue();
  134. /**
  135. * Returns the <em>preferred</em> value of this <code>Spring</code>.
  136. *
  137. * @return the <code>preferredValue</code> of this <code>Spring</code>
  138. */
  139. public abstract int getPreferredValue();
  140. /**
  141. * Returns the <em>maximum</em> value of this <code>Spring</code>.
  142. *
  143. * @return the <code>maximumValue</code> property of this <code>Spring</code>
  144. */
  145. public abstract int getMaximumValue();
  146. /**
  147. * Returns the current <em>value</em> of this <code>Spring</code>.
  148. *
  149. * @return the <code>value</code> property of this <code>Spring</code>
  150. *
  151. * @see #setValue
  152. */
  153. public abstract int getValue();
  154. /**
  155. * Sets the current <em>value</em> of this <code>Spring</code> to <code>value</code>.
  156. *
  157. * @param value the new setting of the <code>value</code> property
  158. *
  159. * @see #getValue
  160. */
  161. public abstract void setValue(int value);
  162. private double range(boolean contract) {
  163. return contract ? (getPreferredValue() - getMinimumValue()) :
  164. (getMaximumValue() - getPreferredValue());
  165. }
  166. /*pp*/ double getStrain() {
  167. double delta = (getValue() - getPreferredValue());
  168. return deltarange(getValue() < getPreferredValue());
  169. }
  170. /*pp*/ void setStrain(double strain) {
  171. setValue(getPreferredValue() + (int)(strain * range(strain < 0)));
  172. }
  173. /*pp*/ boolean isCyclic(SpringLayout l) {
  174. return false;
  175. }
  176. /*pp*/ static abstract class AbstractSpring extends Spring {
  177. protected int size = UNSET;
  178. public int getValue() {
  179. return size != UNSET ? size : getPreferredValue();
  180. }
  181. public void setValue(int size) {
  182. if (size == UNSET) {
  183. clear();
  184. return;
  185. }
  186. this.size = size;
  187. }
  188. protected void clear() {
  189. size = UNSET;
  190. }
  191. }
  192. private static class StaticSpring extends AbstractSpring {
  193. protected int min;
  194. protected int pref;
  195. protected int max;
  196. public StaticSpring() {}
  197. public StaticSpring(int pref) {
  198. this(pref, pref, pref);
  199. }
  200. public StaticSpring(int min, int pref, int max) {
  201. this.min = min;
  202. this.pref = pref;
  203. this.max = max;
  204. this.size = pref;
  205. }
  206. public String toString() {
  207. return "StaticSpring [" + min + ", " + pref + ", " + max + "]";
  208. }
  209. public int getMinimumValue() {
  210. return min;
  211. }
  212. public int getPreferredValue() {
  213. return pref;
  214. }
  215. public int getMaximumValue() {
  216. return max;
  217. }
  218. }
  219. private static class NegativeSpring extends Spring {
  220. private Spring s;
  221. public NegativeSpring(Spring s) {
  222. this.s = s;
  223. }
  224. // Note the use of max value rather than minimum value here.
  225. // See the opening preamble on arithmetic with springs.
  226. public int getMinimumValue() {
  227. return -s.getMaximumValue();
  228. }
  229. public int getPreferredValue() {
  230. return -s.getPreferredValue();
  231. }
  232. public int getMaximumValue() {
  233. return -s.getMinimumValue();
  234. }
  235. public int getValue() {
  236. return -s.getValue();
  237. }
  238. public void setValue(int size) {
  239. // No need to check for UNSET as
  240. // Integer.MIN_VALUE == -Integer.MIN_VALUE.
  241. s.setValue(-size);
  242. }
  243. /*pp*/ boolean isCyclic(SpringLayout l) {
  244. return s.isCyclic(l);
  245. }
  246. }
  247. private static class ScaleSpring extends Spring {
  248. private Spring s;
  249. private float factor;
  250. private ScaleSpring(Spring s, float factor) {
  251. this.s = s;
  252. this.factor = factor;
  253. }
  254. public int getMinimumValue() {
  255. return Math.round((factor < 0 ? s.getMaximumValue() : s.getMinimumValue()) * factor);
  256. }
  257. public int getPreferredValue() {
  258. return Math.round(s.getPreferredValue() * factor);
  259. }
  260. public int getMaximumValue() {
  261. return Math.round((factor < 0 ? s.getMinimumValue() : s.getMaximumValue()) * factor);
  262. }
  263. public int getValue() {
  264. return Math.round(s.getValue() * factor);
  265. }
  266. public void setValue(int value) {
  267. if (value == UNSET) {
  268. s.setValue(UNSET);
  269. } else {
  270. s.setValue(Math.round(value / factor));
  271. }
  272. }
  273. /*pp*/ boolean isCyclic(SpringLayout l) {
  274. return s.isCyclic(l);
  275. }
  276. }
  277. /*pp*/ static class WidthSpring extends AbstractSpring {
  278. /*pp*/ Component c;
  279. public WidthSpring(Component c) {
  280. this.c = c;
  281. }
  282. public int getMinimumValue() {
  283. return c.getMinimumSize().width;
  284. }
  285. public int getPreferredValue() {
  286. return c.getPreferredSize().width;
  287. }
  288. public int getMaximumValue() {
  289. // We will be doing arithmetic with the results of this call,
  290. // so if a returned value is Integer.MAX_VALUE we will get
  291. // arithmetic overflow. Truncate such values.
  292. return Math.min(Short.MAX_VALUE, c.getMaximumSize().width);
  293. }
  294. }
  295. /*pp*/ static class HeightSpring extends AbstractSpring {
  296. /*pp*/ Component c;
  297. public HeightSpring(Component c) {
  298. this.c = c;
  299. }
  300. public int getMinimumValue() {
  301. return c.getMinimumSize().height;
  302. }
  303. public int getPreferredValue() {
  304. return c.getPreferredSize().height;
  305. }
  306. public int getMaximumValue() {
  307. return Math.min(Short.MAX_VALUE, c.getMaximumSize().height);
  308. }
  309. }
  310. // Use the instance variables of the StaticSpring superclass to
  311. // cache values that have already been calculated.
  312. /*pp*/ static abstract class CompoundSpring extends StaticSpring {
  313. protected Spring s1;
  314. protected Spring s2;
  315. public CompoundSpring(Spring s1, Spring s2) {
  316. clear();
  317. this.s1 = s1;
  318. this.s2 = s2;
  319. }
  320. public String toString() {
  321. return "CompoundSpring of " + s1 + " and " + s2;
  322. }
  323. protected void clear() {
  324. min = pref = max = size = UNSET;
  325. }
  326. public void setValue(int size) {
  327. if (size == UNSET) {
  328. if (this.size != UNSET) {
  329. super.setValue(size);
  330. s1.setValue(UNSET);
  331. s2.setValue(UNSET);
  332. return;
  333. }
  334. }
  335. super.setValue(size);
  336. }
  337. protected abstract int op(int x, int y);
  338. public int getMinimumValue() {
  339. if (min == UNSET) {
  340. min = op(s1.getMinimumValue(), s2.getMinimumValue());
  341. }
  342. return min;
  343. }
  344. public int getPreferredValue() {
  345. if (pref == UNSET) {
  346. pref = op(s1.getPreferredValue(), s2.getPreferredValue());
  347. }
  348. return pref;
  349. }
  350. public int getMaximumValue() {
  351. if (max == UNSET) {
  352. max = op(s1.getMaximumValue(), s2.getMaximumValue());
  353. }
  354. return max;
  355. }
  356. public int getValue() {
  357. if (size == UNSET) {
  358. size = op(s1.getValue(), s2.getValue());
  359. }
  360. return size;
  361. }
  362. /*pp*/ boolean isCyclic(SpringLayout l) {
  363. return l.isCyclic(s1) || l.isCyclic(s2);
  364. }
  365. };
  366. private static class SumSpring extends CompoundSpring {
  367. public SumSpring(Spring s1, Spring s2) {
  368. super(s1, s2);
  369. }
  370. protected int op(int x, int y) {
  371. return x + y;
  372. }
  373. public void setValue(int size) {
  374. super.setValue(size);
  375. if (size == UNSET) {
  376. return;
  377. }
  378. s1.setStrain(this.getStrain());
  379. s2.setValue(size - s1.getValue());
  380. }
  381. }
  382. private static class MaxSpring extends CompoundSpring {
  383. public MaxSpring(Spring s1, Spring s2) {
  384. super(s1, s2);
  385. }
  386. protected int op(int x, int y) {
  387. return Math.max(x, y);
  388. }
  389. public void setValue(int size) {
  390. super.setValue(size);
  391. if (size == UNSET) {
  392. return;
  393. }
  394. // Pending should also check max bounds here.
  395. if (s1.getPreferredValue() < s2.getPreferredValue()) {
  396. s1.setValue(Math.min(size, s1.getPreferredValue()));
  397. s2.setValue(size);
  398. }
  399. else {
  400. s1.setValue(size);
  401. s2.setValue(Math.min(size, s2.getPreferredValue()));
  402. }
  403. }
  404. }
  405. /**
  406. * Returns a strut -- a spring whose <em>minimum</em>, <em>preferred</em>, and
  407. * <em>maximum</em> values each have the value <code>pref</code>.
  408. *
  409. * @param pref the <em>minimum</em>, <em>preferred</em>, and
  410. * <em>maximum</em> values of the new spring
  411. * @return a spring whose <em>minimum</em>, <em>preferred</em>, and
  412. * <em>maximum</em> values each have the value <code>pref</code>
  413. *
  414. * @see Spring
  415. */
  416. public static Spring constant(int pref) {
  417. return constant(pref, pref, pref);
  418. }
  419. /**
  420. * Returns a spring whose <em>minimum</em>, <em>preferred</em>, and
  421. * <em>maximum</em> values have the values: <code>min</code>, <code>pref</code>,
  422. * and <code>max</code> respectively.
  423. *
  424. * @param min the <em>minimum</em> value of the new spring
  425. * @param pref the <em>preferred</em> value of the new spring
  426. * @param max the <em>maximum</em> value of the new spring
  427. * @return a spring whose <em>minimum</em>, <em>preferred</em>, and
  428. * <em>maximum</em> values have the values: <code>min</code>, <code>pref</code>,
  429. * and <code>max</code> respectively
  430. *
  431. * @see Spring
  432. */
  433. public static Spring constant(int min, int pref, int max) {
  434. return new StaticSpring(min, pref, max);
  435. }
  436. /**
  437. * Returns <code>-s</code>: a spring running in the opposite direction to <code>s</code>.
  438. *
  439. * @return <code>-s</code>: a spring running in the opposite direction to <code>s</code>
  440. *
  441. * @see Spring
  442. */
  443. public static Spring minus(Spring s) {
  444. return new NegativeSpring(s);
  445. }
  446. /**
  447. * Returns <code>s1+s2</code>: a spring representing <code>s1</code> and <code>s2</code>
  448. * in series. In a sum, <code>s3</code>, of two springs, <code>s1</code> and <code>s2</code>,
  449. * the <em>strains</em> of <code>s1</code>, <code>s2</code>, and <code>s3</code> are maintained
  450. * at the same level (to within the precision implied by their integer <em>value</em>s).
  451. * The strain of a spring in compression is:
  452. * <pre>
  453. * value - pref
  454. * ------------
  455. * pref - min
  456. * </pre>
  457. * and the strain of a spring in tension is:
  458. * <pre>
  459. * value - pref
  460. * ------------
  461. * max - pref
  462. * </pre>
  463. * When <code>setValue</code> is called on the sum spring, <code>s3</code>, the strain
  464. * in <code>s3</code> is calculated using one of the formulas above. Once the strain of
  465. * the sum is known, the <em>value</em>s of <code>s1</code> and <code>s2</code> are
  466. * then set so that they are have a strain equal to that of the sum. The formulas are
  467. * evaluated so as to take rounding errors into account and ensure that the sum of
  468. * the <em>value</em>s of <code>s1</code> and <code>s2</code> is exactly equal to
  469. * the <em>value</em> of <code>s3</code>.
  470. *
  471. * @return <code>s1+s2</code>: a spring representing <code>s1</code> and <code>s2</code> in series
  472. *
  473. * @see Spring
  474. */
  475. public static Spring sum(Spring s1, Spring s2) {
  476. return new SumSpring(s1, s2);
  477. }
  478. /**
  479. * Returns <code>max(s1, s2)</code>: a spring whose value is always greater than (or equal to)
  480. * the values of both <code>s1</code> and <code>s2</code>.
  481. *
  482. * @return <code>max(s1, s2)</code>: a spring whose value is always greater than (or equal to)
  483. * the values of both <code>s1</code> and <code>s2</code>
  484. * @see Spring
  485. */
  486. public static Spring max(Spring s1, Spring s2) {
  487. return new MaxSpring(s1, s2);
  488. }
  489. // Remove these, they're not used often and can be created using minus -
  490. // as per these implementations.
  491. /*pp*/ static Spring difference(Spring s1, Spring s2) {
  492. return sum(s1, minus(s2));
  493. }
  494. /*
  495. public static Spring min(Spring s1, Spring s2) {
  496. return minus(max(minus(s1), minus(s2)));
  497. }
  498. */
  499. /**
  500. * Returns a spring whose <em>minimum</em>, <em>preferred</em>, <em>maximum</em>
  501. * and <em>value</em> properties are each multiples of the properties of the
  502. * argument spring, <code>s</code>. Minimum and maximum properties are
  503. * swapped when <code>factor</code> is negative (in accordance with the
  504. * rules of interval arithmetic).
  505. * <p>
  506. * When factor is, for example, 0.5f the result represents 'the mid-point'
  507. * of its input - an operation that is useful for centering components in
  508. * a container.
  509. *
  510. * @param s the spring to scale
  511. * @param factor amount to scale by.
  512. * @return a spring whose properties are those of the input spring <code>s</code>
  513. * multiplied by <code>factor</code>
  514. * @throws NullPointerException if <code>s</code> is null
  515. * @since 1.5
  516. */
  517. public static Spring scale(Spring s, float factor) {
  518. checkArg(s);
  519. return new ScaleSpring(s, factor);
  520. }
  521. /**
  522. * Returns a spring whose <em>minimum</em>, <em>preferred</em>, <em>maximum</em>
  523. * and <em>value</em> properties are defined by the widths of the <em>minimumSize</em>,
  524. * <em>preferredSize</em>, <em>maximumSize</em> and <em>size</em> properties
  525. * of the supplied component. The returned spring is a 'wrapper' implementation
  526. * whose methods call the appropriate size methods of the supplied component.
  527. * The minimum, preferred, maximum and value properties of the returned spring
  528. * therefore report the current state of the appropriate properties in the
  529. * component and track them as they change.
  530. *
  531. * @param c Component used for calculating size
  532. * @return a spring whose properties are defined by the horizontal component
  533. * of the component's size methods.
  534. * @throws NullPointerException if <code>c</code> is null
  535. * @since 1.5
  536. */
  537. public static Spring width(Component c) {
  538. checkArg(c);
  539. return new WidthSpring(c);
  540. }
  541. /**
  542. * Returns a spring whose <em>minimum</em>, <em>preferred</em>, <em>maximum</em>
  543. * and <em>value</em> properties are defined by the heights of the <em>minimumSize</em>,
  544. * <em>preferredSize</em>, <em>maximumSize</em> and <em>size</em> properties
  545. * of the supplied component. The returned spring is a 'wrapper' implementation
  546. * whose methods call the appropriate size methods of the supplied component.
  547. * The minimum, preferred, maximum and value properties of the returned spring
  548. * therefore report the current state of the appropriate properties in the
  549. * component and track them as they change.
  550. *
  551. * @param c Component used for calculating size
  552. * @return a spring whose properties are defined by the vertical component
  553. * of the component's size methods.
  554. * @throws NullPointerException if <code>c</code> is null
  555. * @since 1.5
  556. */
  557. public static Spring height(Component c) {
  558. checkArg(c);
  559. return new HeightSpring(c);
  560. }
  561. /**
  562. * If <code>s</code> is null, this throws an NullPointerException.
  563. */
  564. private static void checkArg(Object s) {
  565. if (s == null) {
  566. throw new NullPointerException("Argument must not be null");
  567. }
  568. }
  569. }