001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.editor.fold;
043:
044: import org.netbeans.api.editor.fold.Fold;
045: import org.netbeans.modules.editor.fold.ApiPackageAccessor;
046: import org.netbeans.lib.editor.util.GapList;
047:
048: //import org.netbeans.spi.lexer.util.GapObjectArray;
049:
050: /**
051: * Manager of the children of a fold.
052: * <br>
053: *
054: * @author Miloslav Metelka
055: * @version 1.00
056: */
057:
058: public final class FoldChildren extends GapList {
059:
060: /**
061: * Initial size of the index gap.
062: */
063: private static final int INITIAL_INDEX_GAP_LENGTH = Integer.MAX_VALUE >> 1;
064:
065: /**
066: * Parent fold for the folds contained in this children instance.
067: */
068: Fold parent;
069:
070: /**
071: * Index where the index gap resides.
072: */
073: private int indexGapIndex;
074:
075: /**
076: * Length of the index gap in managed folds.
077: * <br>
078: * The initial gap length is chosen big enough
079: * so that it's never reached.
080: */
081: private int indexGapLength;
082:
083: public FoldChildren(Fold parent) {
084: this .parent = parent;
085: indexGapLength = INITIAL_INDEX_GAP_LENGTH;
086: }
087:
088: /**
089: * Get total count of subfolds contained in this fold.
090: *
091: * @return count of subfolds contained in this fold.
092: * Zero means there are no subfolds under this fold.
093: */
094: public int getFoldCount() {
095: return size();
096: }
097:
098: /**
099: * Get fold with the given index.
100: *
101: * @param index >=0 && <{@link #getFoldCount()}
102: * index of the fold.
103: */
104: public Fold getFold(int index) {
105: return (Fold) get(index);
106: }
107:
108: public int getFoldIndex(Fold child) {
109: int index = getTranslatedFoldIndex(ApiPackageAccessor.get()
110: .foldGetRawIndex(child));
111: if (index < 0 || index >= getFoldCount()
112: || getFold(index) != child) {
113: index = -1;
114: }
115: return index;
116: }
117:
118: public Fold[] foldsToArray(int index, int length) {
119: Fold[] folds = new Fold[length];
120: copyElements(index, index + length, folds, 0);
121: return folds;
122: }
123:
124: /**
125: * Insert the given fold at the requested index.
126: */
127: public void insert(int index, Fold fold) {
128: moveIndexGap(index);
129: //ensureCapacity(1);
130: insertImpl(index, fold);
131: }
132:
133: /**
134: * Insert the given folds at the requested index.
135: */
136: public void insert(int index, Fold[] folds) {
137: moveIndexGap(index);
138: insertImpl(index, folds);
139: }
140:
141: public void remove(int index, int length) {
142: moveIndexGap(index + length);
143: for (int i = index + length - 1; i >= index; i--) {
144: ApiPackageAccessor.get().foldSetParent(getFold(i), null);
145: }
146: super .remove(index, length);
147: indexGapLength += length;
148: indexGapIndex -= length;
149: }
150:
151: /**
152: * Extract given area of folds into new FoldChildren instance
153: * parented by the given fold.
154: *
155: * @param index start of the area of folds to be extracted.
156: * @param length length of the area of folds to be extracted.
157: * @param fold fold that will own the newly created fold children.
158: */
159: public FoldChildren extractToChildren(int index, int length,
160: Fold fold) {
161: FoldChildren foldChildren = new FoldChildren(fold);
162: if (length == 1) {
163: Fold insertFold = getFold(index);
164: remove(index, length); // removal prior insertion to set children parents properly
165: foldChildren.insert(0, insertFold);
166: } else {
167: Fold[] insertFolds = foldsToArray(index, length);
168: remove(index, length); // removal prior insertion to set children parents properly
169: foldChildren.insert(0, insertFolds);
170: }
171:
172: // Insert the fold into list of current children
173: insertImpl(index, fold);
174:
175: return foldChildren;
176: }
177:
178: public void replaceByChildren(int index, FoldChildren children) {
179: remove(index, 1);
180:
181: if (children != null) {
182: // Index gap already moved by preceding remove()
183: int childCount = children.getFoldCount();
184: //ensureCapacity(childCount);
185: insertImpl(index, children, 0, childCount);
186: }
187: }
188:
189: private void insertImpl(int index, FoldChildren children,
190: int childIndex, int childCount) {
191:
192: switch (childCount) {
193: case 0: // nothing to do
194: break;
195:
196: case 1: // single item insert
197: insertImpl(index, children.getFold(childIndex));
198: break;
199:
200: default: // multiple items insert
201: Fold[] folds = children
202: .foldsToArray(childIndex, childCount);
203: insertImpl(index, folds);
204: break;
205: }
206: }
207:
208: private void insertImpl(int index, Fold fold) {
209: indexGapLength--;
210: indexGapIndex++;
211: ApiPackageAccessor api = ApiPackageAccessor.get();
212: api.foldSetRawIndex(fold, index);
213: api.foldSetParent(fold, parent);
214: add(index, fold);
215: }
216:
217: private void insertImpl(int index, Fold[] folds) {
218: ApiPackageAccessor api = ApiPackageAccessor.get();
219: int foldsLength = folds.length;
220: indexGapLength -= foldsLength;
221: indexGapIndex += foldsLength;
222: for (int i = foldsLength - 1; i >= 0; i--) {
223: Fold fold = folds[i];
224: api.foldSetRawIndex(fold, index + i);
225: api.foldSetParent(fold, parent);
226: }
227: addArray(index, folds);
228: }
229:
230: private int getTranslatedFoldIndex(int rawIndex) {
231: if (rawIndex >= indexGapLength) {
232: rawIndex -= indexGapLength;
233: }
234: return rawIndex;
235: }
236:
237: private void moveIndexGap(int index) {
238: if (index != indexGapIndex) {
239: ApiPackageAccessor api = ApiPackageAccessor.get();
240: int gapLen = indexGapLength; // cache to local var
241: if (index < indexGapIndex) { // fix back from indexGapIndex till index
242: for (int i = indexGapIndex - 1; i >= index; i--) {
243: api.foldUpdateRawIndex(getFold(i), +gapLen);
244: }
245:
246: } else { // index > indexGapIndex => fix up from indexGapIndex till index
247: for (int i = indexGapIndex; i < index; i++) {
248: api.foldUpdateRawIndex(getFold(i), -gapLen);
249: }
250: }
251: indexGapIndex = index;
252: }
253: }
254:
255: }
|