1. /*
  2. * @(#)SizeSequence.java 1.4 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. /**
  12. * A <code>SizeSequence</code> object
  13. * efficiently maintains an ordered list
  14. * of sizes and corresponding positions.
  15. * One situation for which <code>SizeSequence</code> might be appropriate
  16. * is in a component
  17. * that displays multiple rows of unequal size.
  18. * In this case, a single <code>SizeSequence</code> object could be used
  19. * to track the heights
  20. * and Y positions
  21. * of all rows.
  22. * <p>
  23. * Another example would be a multi-column component,
  24. * such as a JTable,
  25. * in which the column sizes are not all equal.
  26. * The JTable might use a single <code>SizeSequence</code> object
  27. * to store the widths and X positions of all the columns.
  28. * The JTable could then use the <code>SizeSequence</code> object
  29. * to find the column corresponding to a certain position.
  30. * The JTable could update the <code>SizeSequence</code> object
  31. * whenever one or more column sizes changed.
  32. *
  33. * <p>
  34. * The following figure shows the relationship between size and position data
  35. * for a multi-column component.
  36. * <p>
  37. * <center>
  38. * <img src="doc-files/SizeSequence-1.gif" width=384 height = 100
  39. * alt="The first item begins at position 0, the second at the position equal
  40. to the size of the previous item, and so on.">
  41. * </center>
  42. * <p>
  43. * In the figure, the first index (0) corresponds to the first column,
  44. * the second index (1) to the second column, and so on.
  45. * The first column's position starts at 0,
  46. * and the column occupies <em>size<sub>0</sub></em> pixels,
  47. * where <em>size<sub>0</sub></em> is the value returned by
  48. * <code>getSize(0)</code>.
  49. * Thus, the first column ends at <em>size<sub>0</sub></em> - 1.
  50. * The second column then begins at
  51. * the position <em>size<sub>0</sub></em>
  52. * and occupies <em>size<sub>1</sub></em> (<code>getSize(1)</code>) pixels.
  53. * <p>
  54. * Note that a <code>SizeSequence</code> object simply represents intervals
  55. * along an axis.
  56. * In our examples, the intervals represent height or width in pixels.
  57. * However, any other unit of measure (for example, time in days)
  58. * could be just as valid.
  59. *
  60. * <p>
  61. *
  62. * <h4>Implementation Notes</h4>
  63. *
  64. * Normally when storing the size and position of entries,
  65. * one would choose between
  66. * storing the sizes or storing their positions
  67. * instead. The two common operations that are needed during
  68. * rendering are: <code>getIndex(position)</code>
  69. * and <code>setSize(index, size)</code>.
  70. * Whichever choice of internal format is made one of these
  71. * operations is costly when the number of entries becomes large.
  72. * If sizes are stored, finding the index of the entry
  73. * that encloses a particular position is linear in the
  74. * number of entries. If positions are stored instead, setting
  75. * the size of an entry at a particular index requires updating
  76. * the positions of the affected entries, which is also a linear
  77. * calculation.
  78. * <p>
  79. * Like the above techniques this class holds an array of N integers
  80. * internally but uses a hybrid encoding, which is halfway
  81. * between the size-based and positional-based approaches.
  82. * The result is a data structure that takes the same space to store
  83. * the information but can perform most operations in Log(N) time
  84. * instead of O(N), where N is the number of entries in the list.
  85. * <p>
  86. * Two operations that remain O(N) in the number of entries are
  87. * the <code>insertEntries</code>
  88. * and <code>removeEntries</code> methods, both
  89. * of which are implemented by converting the internal array to
  90. * a set of integer sizes, copying it into the new array, and then
  91. * reforming the hybrid representation in place.
  92. *
  93. * @version 1.4 02/02/00
  94. * @author Philip Milne
  95. */
  96. /*
  97. * Each method is implemented by taking the minimum and
  98. * maximum of the range of integers that need to be operated
  99. * upon. All the algorithms work by dividing this range
  100. * into two smaller ranges and recursing. The recursion
  101. * is terminated when the upper and lower bounds are equal.
  102. */
  103. public class SizeSequence {
  104. private static int[] emptyArray = new int[0];
  105. private int a[];
  106. /**
  107. * Creates a new <code>SizeSequence</code> object
  108. * that contains no entries. To add entries, you
  109. * can use <code>insertEntries</code> or <code>setSizes</code>.
  110. *
  111. * @see #insertEntries
  112. * @see #setSizes
  113. */
  114. public SizeSequence() {
  115. a = emptyArray;
  116. }
  117. /**
  118. * Creates a new <code>SizeSequence</code> object
  119. * that contains the specified number of entries,
  120. * all initialized to have size 0.
  121. *
  122. * @param numEntries the number of sizes to track
  123. */
  124. public SizeSequence(int numEntries) {
  125. this(numEntries, 0);
  126. }
  127. /**
  128. * Creates a new <code>SizeSequence</code> object
  129. * that contains the specified number of entries,
  130. * all initialized to have size <code>value</code>.
  131. *
  132. * @param numEntries the number of sizes to track
  133. * @param value the initial value of each size
  134. */
  135. public SizeSequence(int numEntries, int value) {
  136. this();
  137. insertEntries(0, numEntries, value);
  138. }
  139. /**
  140. * Creates a new <code>SizeSequence</code> object
  141. * that contains the specified sizes.
  142. *
  143. * @param sizes the array of sizes to be contained in
  144. * the <code>SizeSequence</code>
  145. */
  146. public SizeSequence(int[] sizes) {
  147. this();
  148. setSizes(sizes);
  149. }
  150. /**
  151. * Resets this <code>SizeSequence</code> object,
  152. * using the data in the <code>sizes</code> argument.
  153. * This method reinitializes
  154. * this object
  155. * so that it contains as many entries as the <code>sizes</code> array.
  156. * Each entry's size is initialized
  157. * to the value of the corresponding
  158. * item in <code>sizes</code>.
  159. *
  160. * @param sizes the array of sizes to be contained in
  161. * this <code>SizeSequence</code>
  162. */
  163. public void setSizes(int[] sizes) {
  164. if (a.length != sizes.length) {
  165. a = new int[sizes.length];
  166. }
  167. setSizes(0, a.length, sizes);
  168. }
  169. private int setSizes(int from, int to, int[] sizes) {
  170. if (to <= from) {
  171. return 0;
  172. }
  173. int m = (from + to)/2;
  174. a[m] = sizes[m] + setSizes(from, m, sizes);
  175. return a[m] + setSizes(m + 1, to, sizes);
  176. }
  177. /**
  178. * Returns the size of all entries.
  179. *
  180. * @return a new array containing the sizes in this object
  181. */
  182. public int[] getSizes() {
  183. int n = a.length;
  184. int[] sizes = new int[n];
  185. getSizes(0, n, sizes);
  186. return sizes;
  187. }
  188. private int getSizes(int from, int to, int[] sizes) {
  189. if (to <= from) {
  190. return 0;
  191. }
  192. int m = (from + to)/2;
  193. sizes[m] = a[m] - getSizes(from, m, sizes);
  194. return a[m] + getSizes(m + 1, to, sizes);
  195. }
  196. /**
  197. * Returns the start position for the specified entry.
  198. * For example, <code>getPosition(0)</code> returns 0,
  199. * <code>getPosition(1)</code> is equal to
  200. * <code>getSize(0)</code>,
  201. * <code>getPosition(2)</code> is equal to
  202. * <code>getSize(0)</code> + <code>getSize(1)</code>,
  203. * and so on.
  204. *
  205. * @param index the index of the entry whose position is desired
  206. * @return the starting position of the specified entry
  207. */
  208. public int getPosition(int index) {
  209. return getPosition(0, a.length, index);
  210. }
  211. private int getPosition(int from, int to, int index) {
  212. if (to <= from) {
  213. return 0;
  214. }
  215. int m = (from + to)/2;
  216. if (index <= m) {
  217. return getPosition(from, m, index);
  218. }
  219. else {
  220. return a[m] + getPosition(m + 1, to, index);
  221. }
  222. }
  223. /**
  224. * Returns the index of the entry
  225. * that corresponds to the specified position.
  226. * For example, <code>getIndex(0)</code> is 0,
  227. * since the first entry always starts at position 0.
  228. *
  229. * @param position the position of the entry
  230. * @return the index of the entry that occupies the specified position
  231. */
  232. public int getIndex(int position) {
  233. return getIndex(0, a.length, position);
  234. }
  235. private int getIndex(int from, int to, int position) {
  236. if (to <= from) {
  237. return from;
  238. }
  239. int m = (from + to)/2;
  240. int pivot = a[m];
  241. if (position < pivot) {
  242. return getIndex(from, m, position);
  243. }
  244. else {
  245. return getIndex(m + 1, to, position - pivot);
  246. }
  247. }
  248. /**
  249. * Returns the size of the specified entry.
  250. *
  251. * @param index the index corresponding to the entry
  252. * @return the size of the entry
  253. */
  254. public int getSize(int index) {
  255. return getPosition(index + 1) - getPosition(index);
  256. }
  257. /**
  258. * Sets the size of the specified entry.
  259. *
  260. * @param index the index corresponding to the entry
  261. * @param size the size of the entry
  262. */
  263. public void setSize(int index, int size) {
  264. changeSize(0, a.length, index, size - getSize(index));
  265. }
  266. private void changeSize(int from, int to, int index, int delta) {
  267. if (to <= from) {
  268. return;
  269. }
  270. int m = (from + to)/2;
  271. if (index <= m) {
  272. a[m] += delta;
  273. changeSize(from, m, index, delta);
  274. }
  275. else {
  276. changeSize(m + 1, to, index, delta);
  277. }
  278. }
  279. /**
  280. * Adds a contiguous group of entries to this <code>SizeSequence</code>.
  281. *
  282. * @param start the index to be assigned to the first entry
  283. * in the group
  284. * @param length the number of entries in the group
  285. * @param value the size to be assigned to each new entry
  286. */
  287. public void insertEntries(int start, int length, int value) {
  288. int sizes[] = getSizes();
  289. int end = start + length;
  290. int n = a.length + length;
  291. a = new int[n];
  292. for (int i = 0; i < start; i++) {
  293. a[i] = sizes[i] ;
  294. }
  295. for (int i = start; i < end; i++) {
  296. a[i] = value ;
  297. }
  298. for (int i = end; i < n; i++) {
  299. a[i] = sizes[i-length] ;
  300. }
  301. setSizes(a);
  302. }
  303. /**
  304. * Removes a contiguous group of entries
  305. * from this <code>SizeSequence</code>.
  306. *
  307. * @param start the index of the first entry to be removed
  308. * @param length the number of entries to be removed
  309. */
  310. public void removeEntries(int start, int length) {
  311. int sizes[] = getSizes();
  312. int end = start + length;
  313. int n = a.length - length;
  314. a = new int[n];
  315. for (int i = 0; i < start; i++) {
  316. a[i] = sizes[i] ;
  317. }
  318. for (int i = start; i < n; i++) {
  319. a[i] = sizes[i+length] ;
  320. }
  321. setSizes(a);
  322. }
  323. }