1. /*
  2. * @(#)Spring.java 1.6 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.util.*;
  9. /**
  10. * An instance of the <code>Spring</code> class holds three properties that
  11. * characterize its behavior: the <em>minimum</em>, <em>preferred</em>, and
  12. * <em>maximum</em> values. Each of these properties may be involved in
  13. * defining its fourth, <em>value</em>, property based on a series of rules.
  14. * <p>
  15. * An instance of the <code>Spring</code> class can be visualized as a
  16. * mechanical spring that provides a corrective force as the spring is compressed
  17. * or stretched away from its preferred value. This force is modelled
  18. * as linear function of the distance from the preferred value, but with
  19. * two different constants -- one for the compressional force and one for the
  20. * tensional one. Those constants are specified by the minimum and maximum
  21. * values of the spring such that a spring at its minimum value produces an
  22. * equal and opposite force to that which is created when it is at its
  23. * maximum value. The difference between the <em>preferred</em> and
  24. * <em>minimum</em> values, therefore, represents the ease with which the
  25. * spring can be compressed and the difference between its <em>maximum</em>
  26. * and <em>preferred</em> values, indicates the ease with which the
  27. * <code>Spring</code> can be extended.
  28. * See the {@link #sum} method for details.
  29. *
  30. * <p>
  31. * By defining simple arithmetic operations on <code>Spring</code>s,
  32. * the behavior of a collection of <code>Spring</code>s
  33. * can be reduced to that of an ordinary (non-compound) <code>Spring</code>. We define
  34. * the "+", "-", <em>max</em>, and <em>min</em> operators on
  35. * <code>Spring</code>s so that, in each case, the result is a <code>Spring</code>
  36. * whose characteristics bear a useful mathematical relationship to its constituent
  37. * springs.
  38. *
  39. * <p>
  40. * A <code>Spring</code> can be treated as a pair of intervals
  41. * with a single common point: the preferred value.
  42. * The following rules define some of the
  43. * arithmetic operators that can be applied to intervals
  44. * (<code>[a, b]</code> refers to the interval
  45. * from <code>a</code>
  46. * to <code>b</code>,
  47. * where <code>a <= b</code>).
  48. * <p>
  49. * <pre>
  50. * [a1, b1] + [a2, b2] = [a1 + a2, b1 + b2]
  51. *
  52. * -[a, b] = [-b, -a]
  53. *
  54. * max([a1, b1], [a2, b2]) = [max(a1, a2), max(b1, b2)]
  55. * </pre>
  56. * <p>
  57. *
  58. * If we denote <code>Spring</code>s as <code>[a, b, c]</code>,
  59. * where <code>a <= b <= c</code>, we can define the same
  60. * arithmetic operators on <code>Spring</code>s:
  61. * <p>
  62. * <pre>
  63. * [a1, b1, c1] + [a2, b2, c2] = [a1 + a2, b1 + b2, c1 + c2]
  64. *
  65. * -[a, b, c] = [-c, -b, -a]
  66. *
  67. * max([a1, b1, c1], [a2, b2, c2]) = [max(a1, a2), max(b1, b2), max(c1, c2)]
  68. * </pre>
  69. * <p>
  70. * With both intervals and <code>Spring</code>s we can define "-" and <em>min</em>
  71. * in terms of negation:
  72. * <p>
  73. * <pre>
  74. * X - Y = X + (-Y)
  75. *
  76. * min(X, Y) = -max(-X, -Y)
  77. * </pre>
  78. * <p>
  79. * For the static methods in this class that embody the arithmetic
  80. * operators, we do not actually perform the operation in question as
  81. * that would snapshot the values of the properties of the method's arguments
  82. * at the time the static method is called. Instead, the static methods
  83. * create a new <code>Spring</code> instance containing references to
  84. * the method's arguments so that the characteristics of the new spring track the
  85. * potentially changing characteristics of the springs from which it
  86. * was made. This is a little like the idea of a <em>lazy value</em>
  87. * in a functional language.
  88. *
  89. * @see SpringLayout
  90. * @see SpringLayout.Constraints
  91. *
  92. * @version 1.6 01/23/03
  93. * @author Philip Milne
  94. * @since 1.4
  95. */
  96. public abstract class Spring {
  97. /**
  98. * An integer value signifying that a property value has not yet been calculated.
  99. */
  100. public static final int UNSET = Integer.MIN_VALUE;
  101. /**
  102. * Used by factory methods to create a <code>Spring</code>.
  103. *
  104. * @see #constant(int)
  105. * @see #constant(int, int, int)
  106. * @see #max
  107. * @see #minus
  108. * @see #sum
  109. * @see SpringLayout.Constraints
  110. */
  111. protected Spring() {}
  112. /**
  113. * Returns the <em>minimum</em> value of this <code>Spring</code>.
  114. *
  115. * @return the <code>minimumValue</code> property of this <code>Spring</code>
  116. */
  117. public abstract int getMinimumValue();
  118. /**
  119. * Returns the <em>preferred</em> value of this <code>Spring</code>.
  120. *
  121. * @return the <code>preferredValue</code> of this <code>Spring</code>
  122. */
  123. public abstract int getPreferredValue();
  124. /**
  125. * Returns the <em>maximum</em> value of this <code>Spring</code>.
  126. *
  127. * @return the <code>maximumValue</code> property of this <code>Spring</code>
  128. */
  129. public abstract int getMaximumValue();
  130. /**
  131. * Returns the current <em>value</em> of this <code>Spring</code>.
  132. *
  133. * @return the <code>value</code> property of this <code>Spring</code>
  134. *
  135. * @see #setValue
  136. */
  137. public abstract int getValue();
  138. /**
  139. * Sets the current <em>value</em> of this <code>Spring</code> to <code>value</code>.
  140. *
  141. * @param value the new setting of the <code>value</code> property
  142. *
  143. * @see #getValue
  144. */
  145. public abstract void setValue(int value);
  146. private double range(boolean contract) {
  147. return contract ? (getPreferredValue() - getMinimumValue()) :
  148. (getMaximumValue() - getPreferredValue());
  149. }
  150. /*pp*/ double getStrain() {
  151. double delta = (getValue() - getPreferredValue());
  152. return deltarange(getValue() < getPreferredValue());
  153. }
  154. /*pp*/ void setStrain(double strain) {
  155. setValue(getPreferredValue() + (int)(strain * range(strain < 0)));
  156. }
  157. /*pp*/ boolean isCyclic(SpringLayout l) {
  158. return false;
  159. }
  160. /*pp*/ static abstract class AbstractSpring extends Spring {
  161. protected int size = UNSET;
  162. public int getValue() {
  163. return size != UNSET ? size : getPreferredValue();
  164. }
  165. public void setValue(int size) {
  166. if (size == UNSET) {
  167. clear();
  168. return;
  169. }
  170. this.size = size;
  171. }
  172. protected void clear() {
  173. size = UNSET;
  174. }
  175. }
  176. private static class StaticSpring extends AbstractSpring {
  177. protected int min;
  178. protected int pref;
  179. protected int max;
  180. public StaticSpring() {}
  181. public StaticSpring(int pref) {
  182. this(pref, pref, pref);
  183. }
  184. public StaticSpring(int min, int pref, int max) {
  185. this.min = min;
  186. this.pref = pref;
  187. this.max = max;
  188. this.size = pref;
  189. }
  190. public String toString() {
  191. return "StaticSpring [" + min + ", " + pref + ", " + max + "]";
  192. }
  193. public int getMinimumValue() {
  194. return min;
  195. }
  196. public int getPreferredValue() {
  197. return pref;
  198. }
  199. public int getMaximumValue() {
  200. return max;
  201. }
  202. }
  203. private static class NegativeSpring extends Spring {
  204. private Spring s;
  205. public NegativeSpring(Spring s) {
  206. this.s = s;
  207. }
  208. // Note the use of max value rather than minimum value here.
  209. // See the opening preamble on arithmetic with springs.
  210. public int getMinimumValue() {
  211. return -s.getMaximumValue();
  212. }
  213. public int getPreferredValue() {
  214. return -s.getPreferredValue();
  215. }
  216. public int getMaximumValue() {
  217. return -s.getMinimumValue();
  218. }
  219. public int getValue() {
  220. return -s.getValue();
  221. }
  222. public void setValue(int size) {
  223. // No need to check for UNSET as
  224. // Integer.MIN_VALUE == -Integer.MIN_VALUE.
  225. s.setValue(-size);
  226. }
  227. /*pp*/ boolean isCyclic(SpringLayout l) {
  228. return s.isCyclic(l);
  229. }
  230. }
  231. // Use the instance variables of the StaticSpring superclass to
  232. // cache values that have already been calculated.
  233. /*pp*/ static abstract class CompoundSpring extends StaticSpring {
  234. protected Spring s1;
  235. protected Spring s2;
  236. public CompoundSpring(Spring s1, Spring s2) {
  237. clear();
  238. this.s1 = s1;
  239. this.s2 = s2;
  240. }
  241. public String toString() {
  242. return "CompoundSpring of " + s1 + " and " + s2;
  243. }
  244. protected void clear() {
  245. min = pref = max = size = UNSET;
  246. }
  247. public void setValue(int size) {
  248. if (size == UNSET) {
  249. if (this.size != UNSET) {
  250. super.setValue(size);
  251. s1.setValue(UNSET);
  252. s2.setValue(UNSET);
  253. return;
  254. }
  255. }
  256. super.setValue(size);
  257. }
  258. protected abstract int op(int x, int y);
  259. public int getMinimumValue() {
  260. if (min == UNSET) {
  261. min = op(s1.getMinimumValue(), s2.getMinimumValue());
  262. }
  263. return min;
  264. }
  265. public int getPreferredValue() {
  266. if (pref == UNSET) {
  267. pref = op(s1.getPreferredValue(), s2.getPreferredValue());
  268. }
  269. return pref;
  270. }
  271. public int getMaximumValue() {
  272. if (max == UNSET) {
  273. max = op(s1.getMaximumValue(), s2.getMaximumValue());
  274. }
  275. return max;
  276. }
  277. public int getValue() {
  278. if (size == UNSET) {
  279. size = op(s1.getValue(), s2.getValue());
  280. }
  281. return size;
  282. }
  283. /*pp*/ boolean isCyclic(SpringLayout l) {
  284. return l.isCyclic(s1) || l.isCyclic(s2);
  285. }
  286. };
  287. private static class SumSpring extends CompoundSpring {
  288. public SumSpring(Spring s1, Spring s2) {
  289. super(s1, s2);
  290. }
  291. protected int op(int x, int y) {
  292. return x + y;
  293. }
  294. public void setValue(int size) {
  295. super.setValue(size);
  296. if (size == UNSET) {
  297. return;
  298. }
  299. s1.setStrain(this.getStrain());
  300. s2.setValue(size - s1.getValue());
  301. }
  302. }
  303. private static class MaxSpring extends CompoundSpring {
  304. public MaxSpring(Spring s1, Spring s2) {
  305. super(s1, s2);
  306. }
  307. protected int op(int x, int y) {
  308. return Math.max(x, y);
  309. }
  310. public void setValue(int size) {
  311. super.setValue(size);
  312. if (size == UNSET) {
  313. return;
  314. }
  315. // Pending should also check max bounds here.
  316. if (s1.getPreferredValue() < s2.getPreferredValue()) {
  317. s1.setValue(Math.min(size, s1.getPreferredValue()));
  318. s2.setValue(size);
  319. }
  320. else {
  321. s1.setValue(size);
  322. s2.setValue(Math.min(size, s2.getPreferredValue()));
  323. }
  324. }
  325. }
  326. /**
  327. * Returns a strut -- a spring whose <em>minimum</em>, <em>preferred</em>, and
  328. * <em>maximum</em> values each have the value <code>pref</code>.
  329. *
  330. * @param pref the <em>minimum</em>, <em>preferred</em>, and
  331. * <em>maximum</em> values of the new spring
  332. * @return a spring whose <em>minimum</em>, <em>preferred</em>, and
  333. * <em>maximum</em> values each have the value <code>pref</code>
  334. *
  335. * @see Spring
  336. */
  337. public static Spring constant(int pref) {
  338. return constant(pref, pref, pref);
  339. }
  340. /**
  341. * Returns a spring whose <em>minimum</em>, <em>preferred</em>, and
  342. * <em>maximum</em> values have the values: <code>min</code>, <code>pref</code>,
  343. * and <code>max</code> respectively.
  344. *
  345. * @param min the <em>minimum</em> value of the new spring
  346. * @param pref the <em>preferred</em> value of the new spring
  347. * @param max the <em>maximum</em> value of the new spring
  348. * @return a spring whose <em>minimum</em>, <em>preferred</em>, and
  349. * <em>maximum</em> values have the values: <code>min</code>, <code>pref</code>,
  350. * and <code>max</code> respectively
  351. *
  352. * @see Spring
  353. */
  354. public static Spring constant(int min, int pref, int max) {
  355. return new StaticSpring(min, pref, max);
  356. }
  357. /**
  358. * Returns <code>-s</code>: a spring running in the opposite direction to <code>s</code>.
  359. *
  360. * @return <code>-s</code>: a spring running in the opposite direction to <code>s</code>
  361. *
  362. * @see Spring
  363. */
  364. public static Spring minus(Spring s) {
  365. return new NegativeSpring(s);
  366. }
  367. /**
  368. * Returns <code>s1+s2</code>: a spring representing <code>s1</code> and <code>s2</code>
  369. * in series. In a sum, <code>s3</code>, of two springs, <code>s1</code> and <code>s2</code>,
  370. * the <em>strains</em> of <code>s1</code>, <code>s2</code>, and <code>s3</code> are maintained
  371. * at the same level (to within the precision implied by their integer <em>value</em>s).
  372. * The strain of a spring in compression is:
  373. * <pre>
  374. * value - pref
  375. * ------------
  376. * pref - min
  377. * </pre>
  378. * and the strain of a spring in tension is:
  379. * <pre>
  380. * value - pref
  381. * ------------
  382. * max - pref
  383. * </pre>
  384. * When <code>setValue</code> is called on the sum spring, <code>s3</code>, the strain
  385. * in <code>s3</code> is calculated using one of the formulas above. Once the strain of
  386. * the sum is known, the <em>value</em>s of <code>s1</code> and <code>s2</code> are
  387. * then set so that they are have a strain equal to that of the sum. The formulas are
  388. * evaluated so as to take rounding errors into account and ensure that the sum of
  389. * the <em>value</em>s of <code>s1</code> and <code>s2</code> is exactly equal to
  390. * the <em>value</em> of <code>s3</code>.
  391. *
  392. * @return <code>s1+s2</code>: a spring representing <code>s1</code> and <code>s2</code> in series
  393. *
  394. * @see Spring
  395. */
  396. public static Spring sum(Spring s1, Spring s2) {
  397. return new SumSpring(s1, s2);
  398. }
  399. /**
  400. * Returns <code>max(s1, s2)</code>: a spring whose value is always greater than (or equal to)
  401. * the values of both <code>s1</code> and <code>s2</code>.
  402. *
  403. * @return <code>max(s1, s2)</code>: a spring whose value is always greater than (or equal to)
  404. * the values of both <code>s1</code> and <code>s2</code>
  405. * @see Spring
  406. */
  407. public static Spring max(Spring s1, Spring s2) {
  408. return new MaxSpring(s1, s2);
  409. }
  410. // Remove these, they're not used often and can be created using minus -
  411. // as per these implementations.
  412. /*pp*/ static Spring difference(Spring s1, Spring s2) {
  413. return sum(s1, minus(s2));
  414. }
  415. /*
  416. public static Spring min(Spring s1, Spring s2) {
  417. return minus(max(minus(s1), minus(s2)));
  418. }
  419. */
  420. }