1. /*
  2. * @(#)GapVector.java 1.12 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.text;
  8. import java.util.Vector;
  9. import java.io.Serializable;
  10. import javax.swing.undo.UndoableEdit;
  11. /**
  12. * An implementation of a gapped buffer similar to that used by
  13. * emacs. The underlying storage is a java array of some type,
  14. * which is known only by the subclass of this class. The array
  15. * has a gap somewhere. The gap is moved to the location of changes
  16. * to take advantage of common behavior where most changes occur
  17. * in the same location. Changes that occur at a gap boundary are
  18. * generally cheap and moving the gap is generally cheaper than
  19. * moving the array contents directly to accomodate the change.
  20. *
  21. * @author Timothy Prinzing
  22. * @version 1.12 12/19/03
  23. * @see GapContent
  24. */
  25. abstract class GapVector implements Serializable {
  26. /**
  27. * Creates a new GapVector object. Initial size defaults to 10.
  28. */
  29. public GapVector() {
  30. this(10);
  31. }
  32. /**
  33. * Creates a new GapVector object, with the initial
  34. * size specified.
  35. *
  36. * @param initialLength the initial size
  37. */
  38. public GapVector(int initialLength) {
  39. array = allocateArray(initialLength);
  40. g0 = 0;
  41. g1 = initialLength;
  42. }
  43. /**
  44. * Allocate an array to store items of the type
  45. * appropriate (which is determined by the subclass).
  46. */
  47. protected abstract Object allocateArray(int len);
  48. /**
  49. * Get the length of the allocated array
  50. */
  51. protected abstract int getArrayLength();
  52. /**
  53. * Access to the array. The actual type
  54. * of the array is known only by the subclass.
  55. */
  56. protected final Object getArray() {
  57. return array;
  58. }
  59. /**
  60. * Access to the start of the gap.
  61. */
  62. protected final int getGapStart() {
  63. return g0;
  64. }
  65. /**
  66. * Access to the end of the gap.
  67. */
  68. protected final int getGapEnd() {
  69. return g1;
  70. }
  71. // ---- variables -----------------------------------
  72. /**
  73. * The array of items. The type is determined by the subclass.
  74. */
  75. private Object array;
  76. /**
  77. * start of gap in the array
  78. */
  79. private int g0;
  80. /**
  81. * end of gap in the array
  82. */
  83. private int g1;
  84. // --- gap management -------------------------------
  85. /**
  86. * Replace the given logical position in the storage with
  87. * the given new items. This will move the gap to the area
  88. * being changed if the gap is not currently located at the
  89. * change location.
  90. *
  91. * @param position the location to make the replacement. This
  92. * is not the location in the underlying storage array, but
  93. * the location in the contiguous space being modeled.
  94. * @param rmSize the number of items to remove
  95. * @param addItems the new items to place in storage.
  96. */
  97. protected void replace(int position, int rmSize, Object addItems, int addSize) {
  98. int addOffset = 0;
  99. if (addSize == 0) {
  100. close(position, rmSize);
  101. return;
  102. } else if (rmSize > addSize) {
  103. /* Shrink the end. */
  104. close(position+addSize, rmSize-addSize);
  105. } else {
  106. /* Grow the end, do two chunks. */
  107. int endSize = addSize - rmSize;
  108. int end = open(position + rmSize, endSize);
  109. System.arraycopy(addItems, rmSize, array, end, endSize);
  110. addSize = rmSize;
  111. }
  112. System.arraycopy(addItems, addOffset, array, position, addSize);
  113. }
  114. /**
  115. * Delete nItems at position. Squeezes any marks
  116. * within the deleted area to position. This moves
  117. * the gap to the best place by minimizing it's
  118. * overall movement. The gap must intersect the
  119. * target block.
  120. */
  121. void close(int position, int nItems) {
  122. if (nItems == 0) return;
  123. int end = position + nItems;
  124. int new_gs = (g1 - g0) + nItems;
  125. if (end <= g0) {
  126. // Move gap to end of block.
  127. if (g0 != end) {
  128. shiftGap(end);
  129. }
  130. // Adjust g0.
  131. shiftGapStartDown(g0 - nItems);
  132. } else if (position >= g0) {
  133. // Move gap to beginning of block.
  134. if (g0 != position) {
  135. shiftGap(position);
  136. }
  137. // Adjust g1.
  138. shiftGapEndUp(g0 + new_gs);
  139. } else {
  140. // The gap is properly inside the target block.
  141. // No data movement necessary, simply move both gap pointers.
  142. shiftGapStartDown(position);
  143. shiftGapEndUp(g0 + new_gs);
  144. }
  145. }
  146. /**
  147. * Make space for the given number of items at the given
  148. * location.
  149. *
  150. * @return the location that the caller should fill in
  151. */
  152. int open(int position, int nItems) {
  153. int gapSize = g1 - g0;
  154. if (nItems == 0) {
  155. if (position > g0)
  156. position += gapSize;
  157. return position;
  158. }
  159. // Expand the array if the gap is too small.
  160. shiftGap(position);
  161. if (nItems >= gapSize) {
  162. // Pre-shift the gap, to reduce total movement.
  163. shiftEnd(getArrayLength() - gapSize + nItems);
  164. gapSize = g1 - g0;
  165. }
  166. g0 = g0 + nItems;
  167. return position;
  168. }
  169. /**
  170. * resize the underlying storage array to the
  171. * given new size
  172. */
  173. void resize(int nsize) {
  174. Object narray = allocateArray(nsize);
  175. System.arraycopy(array, 0, narray, 0, Math.min(nsize, getArrayLength()));
  176. array = narray;
  177. }
  178. /**
  179. * Make the gap bigger, moving any necessary data and updating
  180. * the appropriate marks
  181. */
  182. protected void shiftEnd(int newSize) {
  183. int oldSize = getArrayLength();
  184. int oldGapEnd = g1;
  185. int upperSize = oldSize - oldGapEnd;
  186. int arrayLength = getNewArraySize(newSize);
  187. int newGapEnd = arrayLength - upperSize;
  188. resize(arrayLength);
  189. g1 = newGapEnd;
  190. if (upperSize != 0) {
  191. // Copy array items to new end of array.
  192. System.arraycopy(array, oldGapEnd, array, newGapEnd, upperSize);
  193. }
  194. }
  195. /**
  196. * Calculates a new size of the storage array depending on required
  197. * capacity.
  198. * @param reqSize the size which is necessary for new content
  199. * @return the new size of the storage array
  200. */
  201. int getNewArraySize(int reqSize) {
  202. return (reqSize + 1) * 2;
  203. }
  204. /**
  205. * Move the start of the gap to a new location,
  206. * without changing the size of the gap. This
  207. * moves the data in the array and updates the
  208. * marks accordingly.
  209. */
  210. protected void shiftGap(int newGapStart) {
  211. if (newGapStart == g0) {
  212. return;
  213. }
  214. int oldGapStart = g0;
  215. int dg = newGapStart - oldGapStart;
  216. int oldGapEnd = g1;
  217. int newGapEnd = oldGapEnd + dg;
  218. int gapSize = oldGapEnd - oldGapStart;
  219. g0 = newGapStart;
  220. g1 = newGapEnd;
  221. if (dg > 0) {
  222. // Move gap up, move data down.
  223. System.arraycopy(array, oldGapEnd, array, oldGapStart, dg);
  224. } else if (dg < 0) {
  225. // Move gap down, move data up.
  226. System.arraycopy(array, newGapStart, array, newGapEnd, -dg);
  227. }
  228. }
  229. /**
  230. * Adjust the gap end downward. This doesn't move
  231. * any data, but it does update any marks affected
  232. * by the boundary change. All marks from the old
  233. * gap start down to the new gap start are squeezed
  234. * to the end of the gap (their location has been
  235. * removed).
  236. */
  237. protected void shiftGapStartDown(int newGapStart) {
  238. g0 = newGapStart;
  239. }
  240. /**
  241. * Adjust the gap end upward. This doesn't move
  242. * any data, but it does update any marks affected
  243. * by the boundary change. All marks from the old
  244. * gap end up to the new gap end are squeezed
  245. * to the end of the gap (their location has been
  246. * removed).
  247. */
  248. protected void shiftGapEndUp(int newGapEnd) {
  249. g1 = newGapEnd;
  250. }
  251. }