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.api.editor.fold;
043:
044: import java.util.Collection;
045: import java.util.Collections;
046: import java.util.Iterator;
047: import java.util.List;
048: import org.netbeans.modules.editor.fold.FoldUtilitiesImpl;
049:
050: /**
051: * Various utility methods for dealing with the folds.
052: *
053: * <p>
054: * <b>Note:</b> Until explicitly noted all the utility methods
055: * require a lock to be held on the {@link FoldHierarchy}
056: * during execution of the methods.
057: *
058: * @author Miloslav Metelka
059: * @version 1.00
060: */
061:
062: public final class FoldUtilities {
063:
064: private FoldUtilities() {
065: // No instances
066: }
067:
068: /**
069: * Is the given fold a root fold?
070: *
071: * @param fold non-null fold which is either root fold or a regular fold.
072: * @return true if the given fold is root fold or false otherwise.
073: */
074: public static boolean isRootFold(Fold fold) {
075: return fold.isRootFold();
076: }
077:
078: /**
079: * Find index of the child of the given fold that
080: * starts right at or below the given offset.
081: *
082: * <p>
083: * This method uses binary search and has log2(n) performance
084: * where n is number of children of the given fold.
085: * <br>
086: * The efficiency may drop to linear if there would be many empty folds
087: * at the given offset.
088: *
089: * @param fold fold which children will be inspected.
090: * @param offset >=0 offset in the document for which the representing
091: * child will be searched.
092: * @return index of the child fold that represents the given offset.
093: * <br>
094: * An <code>index</code> is returned
095: * if <code>offset >= getFold(index).getStartOffset()</code>
096: * and <code>offset <= getFold(index + 1).getStartOffset()</code>.
097: * <br>
098: * <code>-1</code> is returned
099: * if <code>offset < getFold(0).getStartOffset()</code>
100: * and in case the fold does not have any children.
101: */
102: public static int findFoldStartIndex(Fold fold, int offset) {
103: // The empty folds should be removed immediately (prior to notification
104: // to managers) so the "first" param should not matter
105: return FoldUtilitiesImpl.findFoldStartIndex(fold, offset, true);
106: }
107:
108: /**
109: * Find index of the first child of the given fold that ends
110: * above the given offset ("contains" the offset).
111: *
112: * <p>
113: * This method uses binary search and has log2(n) performance
114: * where n is number of children of the given fold.
115: * <br>
116: * The efficiency may drop to linear if there would be many empty folds
117: * at the given offset.
118: *
119: * @param fold fold which children will be inspected.
120: * @param offset >=0 offset in the document for which the representing
121: * child will be searched.
122: * @return index of the child fold that contains or is above the given offset.
123: * <br>
124: * A highest <code>index</code> is returned for which
125: * <code>offset < getFold(index).getEndOffset()</code>
126: * <br>
127: * or <code>fold.getFoldCount()</code> in case there is no such fold.
128: */
129: public static int findFoldEndIndex(Fold fold, int offset) {
130: return FoldUtilitiesImpl.findFoldEndIndex(fold, offset);
131: }
132:
133: /**
134: * Check whether the starting offset of the fold is the same like
135: * its ending offset.
136: *
137: * @param fold fold that should be checked whether it's empty.
138: * @return true if the fold is empty or false otherwise.
139: */
140: public static boolean isEmpty(Fold fold) {
141: return (fold.getStartOffset() == fold.getEndOffset());
142: }
143:
144: /**
145: * Collapse all folds in the hierarchy.
146: * <br>
147: * This method does the necessary locking of the document and hierarchy.
148: *
149: * @param hierarchy hierarchy under which all folds should be collapsed.
150: */
151: public static void collapseAll(FoldHierarchy hierarchy) {
152: collapse(hierarchy, (Collection) null);
153: }
154:
155: /**
156: * Collapse all folds of the given type.
157: * <br>
158: * This method does the necessary locking of the document and hierarchy.
159: *
160: * @param hierarchy hierarchy under which the folds should be collapsed.
161: * @param type folds with this type will be collapsed.
162: */
163: public static void collapse(FoldHierarchy hierarchy, FoldType type) {
164: collapse(hierarchy, Collections.singleton(type));
165: }
166:
167: /**
168: * Collapse all folds that having any
169: * of the fold types in the given collection.
170: * <br>
171: * This method does the necessary locking of the document and hierarchy.
172: *
173: * @param hierarchy hierarchy under which the folds should be collapsed.
174: * @param foldTypes collection of fold types to search for.
175: */
176: public static void collapse(FoldHierarchy hierarchy,
177: Collection foldTypes) {
178: FoldUtilitiesImpl.collapseOrExpand(hierarchy, foldTypes, true);
179: }
180:
181: /**
182: * Expand all folds in the hierarchy.
183: * <br>
184: * This method does the necessary locking of the document and hierarchy.
185: *
186: * @param hierarchy hierarchy under which all folds should be expanded.
187: */
188: public static void expandAll(FoldHierarchy hierarchy) {
189: expand(hierarchy, (Collection) null);
190: }
191:
192: /**
193: * Expand all folds of the given type.
194: * <br>
195: * This method does the necessary locking of the document and hierarchy.
196: *
197: * @param hierarchy hierarchy under which the folds should be expanded.
198: * @param type folds with this type will be expanded.
199: */
200: public static void expand(FoldHierarchy hierarchy, FoldType type) {
201: expand(hierarchy, Collections.singleton(type));
202: }
203:
204: /**
205: * Expand all folds of the given type (or all folds if the type is null)
206: * found in the whole fold hierarchy.
207: * <br>
208: * This method does the necessary locking of the document and hierarchy.
209: *
210: * @param hierarchy hierarchy under which the folds should be expanded.
211: * @param foldTypes collection of fold types to search for.
212: */
213: public static void expand(FoldHierarchy hierarchy,
214: Collection foldTypes) {
215: FoldUtilitiesImpl.collapseOrExpand(hierarchy, foldTypes, false);
216: }
217:
218: /**
219: * Check whether fold contains the given offset.
220: *
221: * @param fold fold to be tested for containing the given offset
222: * @param offset that will be tested for being contained in the given fold.
223: * @return true if <code>offset >= fold.getStartOffset()
224: * && offset < fold.getEndOffset()</code>
225: */
226: public static boolean containsOffset(Fold fold, int offset) {
227: return (offset < fold.getEndOffset() && offset >= fold
228: .getStartOffset());
229: }
230:
231: /**
232: * Return children of the given fold as array.
233: *
234: * @param fold fold which children will be returned.
235: * @return non-null array of all child folds.
236: */
237: public static Fold[] childrenToArray(Fold fold) {
238: return childrenToArray(fold, 0, fold.getFoldCount());
239: }
240:
241: /**
242: * Return children of the given fold as array.
243: *
244: * @param fold fold which children will be returned.
245: * @param index >=0 index of the first child to be returned.
246: * @param count >=0 number of children to be returned.
247: * <code>index + count <= {@link Fold#getFoldCount()}</code>.
248: * @return non-null array of selected child folds.
249: */
250: public static Fold[] childrenToArray(Fold fold, int index, int count) {
251: return fold.foldsToArray(index, count);
252: }
253:
254: /**
255: * Return children of the given fold as modifiable list.
256: * <br>
257: * {@link #findRecursive(Fold)} can be used
258: * to collect children recursively.
259: *
260: * @param fold fold which children will be returned.
261: * @return non-null modifiable list of all child folds.
262: */
263: public static List childrenAsList(Fold fold) {
264: return childrenAsList(fold, 0, fold.getFoldCount());
265: }
266:
267: /**
268: * Return children of the given fold as list.
269: *
270: * @param fold fold which children will be returned.
271: * @param index >=0 index of the first child to be returned.
272: * @param count >=0 number of children to be returned.
273: * <code>index + count <= {@link Fold#getFoldCount()}</code>.
274: * @return non-null list of selected child folds.
275: * <br>
276: * The list can potentially be further modified by the caller without
277: * any effect on the fold hierarchy.
278: */
279: public static List childrenAsList(Fold fold, int index, int count) {
280: return FoldUtilitiesImpl.childrenAsList(fold, index, count);
281: }
282:
283: /**
284: * Find direct subfolds of the given fold having certain type.
285: * <br>
286: * Complexity corresponds to number of direct child folds under the given fold.
287: *
288: * @param fold direct children of this fold will be searched.
289: * The search is *not* recursive in grandchildren etc.
290: * @param foldType non-null fold type to search for.
291: * @return non-null list of folds matching the criteria.
292: * <br>
293: * The list can potentially be further modified by the caller without
294: * any effect on the fold hierarchy.
295: */
296: public static List find(Fold fold, FoldType foldType) {
297: return find(fold, Collections.singletonList(foldType));
298: }
299:
300: /**
301: * Find direct subfolds of the given fold having any
302: * of the fold types in the given collection.
303: * <br>
304: * Complexity corresponds to number of direct child folds under the given fold.
305: *
306: * @param fold direct children of this fold will be searched.
307: * The search is *not* recursive in grandchildren etc.
308: * @param foldTypes collection of fold types to search for.
309: * @return non-null list of folds matching the criteria.
310: * <br>
311: * The list can potentially be further modified by the caller without
312: * any effect on the fold hierarchy.
313: */
314: public static List find(Fold fold, Collection foldTypes) {
315: return FoldUtilitiesImpl.find(fold, foldTypes);
316: }
317:
318: /**
319: * Collect all children of the given fold recursively.
320: * <br>
321: * Complexity corresponds to number of all child folds
322: * (including grandchildren etc.) under the given fold.
323: *
324: * @param fold all children of this fold will be collected.
325: * @return non-null list of folds matching the criteria.
326: * <br>
327: * The list can potentially be further modified by the caller without
328: * any effect on the fold hierarchy.
329: */
330: public static List findRecursive(Fold fold) {
331: return findRecursive(fold, (Collection) null);
332: }
333:
334: /**
335: * Recursively find any subfolds of the given fold having certain type.
336: * <br>
337: * Complexity corresponds to number of all child folds
338: * (including grandchildren etc.) under the given fold.
339: *
340: * @param fold all children of this fold will be searched.
341: * The search is recursive into grandchildren etc.
342: * @param foldType non-null fold type to search for.
343: * @return non-null list of folds matching the criteria.
344: * <br>
345: * The list can potentially be further modified by the caller without
346: * any effect on the fold hierarchy.
347: */
348: public static List findRecursive(Fold fold, FoldType foldType) {
349: return findRecursive(fold, Collections.singletonList(foldType));
350: }
351:
352: /**
353: * Recursively find any subfolds of the given fold having any
354: * of the fold types in the given collection.
355: * <br>
356: * Complexity corresponds to number of all child folds
357: * (including grandchildren etc.) under the given fold.
358: *
359: * @param fold all children of this fold will be searched.
360: * The search is recursive into grandchildren etc.
361: * @param foldTypes collection of fold types to search for.
362: * @return non-null list of folds matching the criteria.
363: * <br>
364: * The list can potentially be further modified by the caller without
365: * any effect on the fold hierarchy.
366: */
367: public static List findRecursive(Fold fold, Collection foldTypes) {
368: return FoldUtilitiesImpl.findRecursive(null, fold, foldTypes);
369: }
370:
371: /**
372: * Find nearest fold that either starts right at or follows
373: * the given offset.
374: * <br>
375: * The search deep-dives into hierarchy.
376: *
377: * @param offset >=0 offset in a document.
378: * @return fold in the hierarchy that starts directly at the offset or follows it.
379: * The most important is the lowest distance of the start of the fold
380: * to the given offset. If there would be a nearest fold having a first child that
381: * starts at the same position like the parent
382: * then the parent would be returned.
383: */
384: public static Fold findNearestFold(FoldHierarchy hierarchy,
385: int offset) {
386: return FoldUtilitiesImpl.findNearestFold(hierarchy, offset,
387: Integer.MAX_VALUE);
388: }
389:
390: /**
391: * Find a deepest fold in the hierarchy which contains the offset
392: * or has it as one of its boundaries.
393: * <br>
394: * The search deep-dives into hierarchy.
395: *
396: * @param offset >=0 offset in a document.
397: * @return deepset fold in the hierarchy satisfying
398: * <code>fold.getStartOffset() >= offset && offset <= fold.getEndOffset()</code>
399: * or null if there is no such fold (except the root fold) satisfying the condition.
400: * <br>
401: * For two consecutive folds (one ending at the offset and the next one
402: * starting at the offset) the latter fold would be returned.
403: */
404: public static Fold findOffsetFold(FoldHierarchy hierarchy,
405: int offset) {
406: return FoldUtilitiesImpl.findOffsetFold(hierarchy, offset);
407: }
408:
409: /**
410: * Find a first collapsed fold by going from top-level folds to more nested ones
411: * within the requested bounds. Once a collapsed fold is found it is returned
412: * (its children even if they would be collapsed are not inspected).
413: *
414: * @param hierarchy hierarchy in which to search.
415: * @param startOffset >=0 only fold ending above it will be returned.
416: * @param endOffset >=0 only fold starting below it will be returned.
417: * @return collapsed fold satisfying
418: * <code>fold.getEndOffset() > startOffset and fold.getStartOffset() < endOffset</code>
419: * or null if such fold does not exist.
420: */
421: public static Fold findCollapsedFold(FoldHierarchy hierarchy,
422: int startOffset, int endOffset) {
423:
424: return FoldUtilitiesImpl.findFirstCollapsedFold(hierarchy,
425: startOffset, endOffset);
426: }
427:
428: /**
429: * Get iterator over the collapsed folds.
430: *
431: * @param hierarchy hierarchy in which to search.
432: * @param startOffset >=0 only folds ending above it will be returned.
433: * @param endOffset >=0 only folds starting before it will be returned.
434: * @return iterator over collapsed folds satisfying
435: * <code>fold.getEndOffset() > startOffset and fold.getStartOffset() < endOffset</code>
436: * <br>
437: * If a particular collapsed fold gets returned then its children
438: * are not deep-dived for collapsed folds. Instead the search continues
439: * by a following sibling.
440: */
441: public static Iterator collapsedFoldIterator(
442: FoldHierarchy hierarchy, int startOffset, int endOffset) {
443:
444: return FoldUtilitiesImpl.collapsedFoldIterator(hierarchy,
445: startOffset, endOffset);
446: }
447:
448: }
|