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