1. /*
  2. * @(#)GapVector.java 1.10 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.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.10 01/23/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 = (newSize + 1) * 2;
  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. * Move the start of the gap to a new location,
  197. * without changing the size of the gap. This
  198. * moves the data in the array and updates the
  199. * marks accordingly.
  200. */
  201. protected void shiftGap(int newGapStart) {
  202. if (newGapStart == g0) {
  203. return;
  204. }
  205. int oldGapStart = g0;
  206. int dg = newGapStart - oldGapStart;
  207. int oldGapEnd = g1;
  208. int newGapEnd = oldGapEnd + dg;
  209. int gapSize = oldGapEnd - oldGapStart;
  210. g0 = newGapStart;
  211. g1 = newGapEnd;
  212. if (dg > 0) {
  213. // Move gap up, move data down.
  214. System.arraycopy(array, oldGapEnd, array, oldGapStart, dg);
  215. } else if (dg < 0) {
  216. // Move gap down, move data up.
  217. System.arraycopy(array, newGapStart, array, newGapEnd, -dg);
  218. }
  219. }
  220. /**
  221. * Adjust the gap end downward. This doesn't move
  222. * any data, but it does update any marks affected
  223. * by the boundary change. All marks from the old
  224. * gap start down to the new gap start are squeezed
  225. * to the end of the gap (their location has been
  226. * removed).
  227. */
  228. protected void shiftGapStartDown(int newGapStart) {
  229. g0 = newGapStart;
  230. }
  231. /**
  232. * Adjust the gap end upward. This doesn't move
  233. * any data, but it does update any marks affected
  234. * by the boundary change. All marks from the old
  235. * gap end up to the new gap end are squeezed
  236. * to the end of the gap (their location has been
  237. * removed).
  238. */
  239. protected void shiftGapEndUp(int newGapEnd) {
  240. g1 = newGapEnd;
  241. }
  242. }