KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > api > editor > fold > Fold


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.api.editor.fold;
21
22 import javax.swing.event.DocumentEvent JavaDoc;
23 import javax.swing.text.BadLocationException JavaDoc;
24 import javax.swing.text.Document JavaDoc;
25 import javax.swing.text.Position JavaDoc;
26 import org.netbeans.modules.editor.fold.FoldOperationImpl;
27 import org.netbeans.modules.editor.fold.FoldChildren;
28 import org.netbeans.modules.editor.fold.FoldUtilitiesImpl;
29 import org.openide.ErrorManager;
30
31 /**
32  * Fold is a building block of the code folding tree-based hierarchy.
33  * <br>
34  * Folds cannot overlap but they can be nested arbitrarily.
35  * <br>
36  * It's possible to determine the fold's type, description
37  * and whether it is collapsed or expanded
38  * and explore the nested (children) folds.
39  * <br>
40  * There are various useful utility methods for folds in the {@link FoldUtilities}.
41  *
42  * <p>
43  * There is one <i>root fold</i> at the top of the code folding hierarchy.
44  * <br>
45  * The root fold is special uncollapsable fold covering the whole document.
46  * <br>
47  * It can be obtained by using {@link FoldHierarchy#getRootFold()}.
48  * <br>
49  * The regular top-level folds are children of the root fold.
50  *
51  * @author Miloslav Metelka
52  * @version 1.00
53  */

54
55 public final class Fold {
56     
57     private static final Fold[] EMPTY_FOLD_ARRAY = new Fold[0];
58     
59     private static final String JavaDoc DEFAULT_DESCRIPTION = "..."; // NOI18N
60

61     private final FoldOperationImpl operation;
62
63     private final FoldType type;
64     
65     private boolean collapsed;
66     
67     private String JavaDoc description;
68     
69     private Fold parent;
70     
71     private FoldChildren children;
72     
73     private int rawIndex;
74     
75     private int startGuardedLength;
76     private int endGuardedLength;
77     
78     private Position JavaDoc startPos;
79     private Position JavaDoc endPos;
80     
81     private Position JavaDoc guardedEndPos;
82     private Position JavaDoc guardedStartPos;
83     
84     private Object JavaDoc extraInfo;
85     
86
87     Fold(FoldOperationImpl operation,
88     FoldType type, String JavaDoc description, boolean collapsed,
89     Document JavaDoc doc, int startOffset, int endOffset,
90     int startGuardedLength, int endGuardedLength,
91     Object JavaDoc extraInfo)
92     throws BadLocationException JavaDoc {
93
94         if (startGuardedLength < 0) {
95             throw new IllegalArgumentException JavaDoc("startGuardedLength=" // NOI18N
96
+ startGuardedLength + " < 0"); // NOI18N
97
}
98         if (endGuardedLength < 0) {
99             throw new IllegalArgumentException JavaDoc("endGuardedLength=" // NOI18N
100
+ endGuardedLength + " < 0"); // NOI18N
101
}
102         if (startOffset == endOffset) {
103             throw new IllegalArgumentException JavaDoc("startOffset == endOffset == " // NOI18N
104
+ startOffset);
105         }
106         if ((endOffset - startOffset) < (startGuardedLength + endGuardedLength)) {
107             throw new IllegalArgumentException JavaDoc("(endOffset=" + endOffset // NOI18N
108
+ " - startOffset=" + startOffset + ") < " // NOI18N
109
+ "(startGuardedLength=" + startGuardedLength // NOI18N
110
+ " + endGuardedLength=" + endGuardedLength + ")" // NOI18N
111
); // NOI18N
112
}
113         
114         this.operation = operation;
115         this.type = type;
116
117         this.collapsed = collapsed;
118         this.description = description;
119
120         this.startPos = doc.createPosition(startOffset);
121         this.endPos = doc.createPosition(endOffset);
122
123         this.startGuardedLength = startGuardedLength;
124         this.endGuardedLength = endGuardedLength;
125         
126         this.extraInfo = extraInfo;
127         
128         // Must assign guarded areas positions
129
// Even if the particular guarded area is zero the particular inner area
130
// has at least 1 character to detect changes leading
131
// to automatic forced expanding of the fold.
132
updateGuardedStartPos(doc, startOffset);
133         updateGuardedEndPos(doc, endOffset);
134
135     }
136
137     /**
138      * Get type of this fold.
139      *
140      * @return non-null type identification of this fold.
141      */

142     public FoldType getType() {
143         return type;
144     }
145     
146     /**
147      * Get parent fold of this fold.
148      *
149      * @return parent fold of this fold or null if this is root fold or if this
150      * fold was removed from the code folding hierarchy.
151      * <br>
152      * {@link FoldUtilities#isRootFold(Fold)} can be used to check
153      * whether this is root fold.
154      */

155     public Fold getParent() {
156         return parent;
157     }
158     
159     void setParent(Fold parent) {
160         if (isRootFold()) {
161             throw new IllegalArgumentException JavaDoc("Cannot set parent on root"); // NOI18N
162
} else {
163             this.parent = parent;
164         }
165     }
166     /**
167      * Get the code folding hierarchy for which this fold was created.
168      *
169      * @return non-null code folding hierarchy for which this fold was constructed.
170      */

171     public FoldHierarchy getHierarchy() {
172         return operation.getHierarchy();
173     }
174     
175     FoldOperationImpl getOperation() {
176         return operation;
177     }
178     
179     /**
180      * Check whether this fold is currently a part of the hierarchy.
181      * <br>
182      * The fold may be temporarily removed from the hierarchy because
183      * it became blocked by another fold. Once the blocking fold gets
184      * removed the original fold becomes a part of the hierarchy again.
185      *
186      * @return true if the fold is actively a part of the hierarchy.
187      */

188 /* public boolean isHierarchyPart() {
189         return (getParent() != null) || isRootFold();
190     }
191  */

192     
193     boolean isRootFold() {
194         return (operation.getManager() == null);
195     }
196     
197     /**
198      * Get an absolute starting offset of this fold in the associated document.
199      * <br>
200      * The starting offset is expected to track possible changes in the underlying
201      * document (i.e. it's maintained
202      * in {@link javax.swing.text.Position}-like form).
203      *
204      * @return &gt;=0 absolute starting offset of this fold in the document.
205      */

206     public int getStartOffset() {
207         return (isRootFold()) ? 0 : startPos.getOffset();
208     }
209     
210     void setStartOffset(Document JavaDoc doc, int startOffset)
211     throws BadLocationException JavaDoc {
212         if (isRootFold()) {
213             throw new IllegalStateException JavaDoc("Cannot set endOffset of root fold"); // NOI18N
214
} else {
215             this.startPos = doc.createPosition(startOffset);
216             updateGuardedStartPos(doc, startOffset);
217         }
218     }
219
220     /**
221      * Get an absolute ending offset of this fold in the associated document.
222      * <br>
223      * The ending offset is expected to track possible changes in the underlying
224      * document (i.e. it's maintained
225      * in {@link javax.swing.text.Position}-like form).
226      *
227      * @return <code>&gt;=getStartOffset()</code>
228      * offset of the first character in the document that is not part
229      * of this fold.
230      */

231     public int getEndOffset() {
232         return isRootFold()
233             ? operation.getHierarchy().getComponent().getDocument().getLength()
234             : endPos.getOffset();
235     }
236     
237     void setEndOffset(Document JavaDoc doc, int endOffset)
238     throws BadLocationException JavaDoc {
239         if (isRootFold()) {
240             throw new IllegalStateException JavaDoc("Cannot set endOffset of root fold"); // NOI18N
241
} else {
242             this.endPos = doc.createPosition(endOffset);
243             updateGuardedEndPos(doc, endOffset);
244         }
245     }
246
247     /**
248      * Return whether this fold is collapsed or expanded.
249      * <br>
250      * To collapse fold {@link FoldHierarchy#collapse(Fold)}
251      * can be used.
252      *
253      * @return true if this fold is collapsed or false if it's expanded.
254      */

255     public boolean isCollapsed() {
256         return collapsed;
257     }
258     
259     void setCollapsed(boolean collapsed) {
260         if (isRootFold()) {
261             throw new IllegalStateException JavaDoc("Cannot set collapsed flag on root fold."); // NOI18N
262
}
263         this.collapsed = collapsed;
264     }
265
266     /**
267      * Get text description that should be displayed when the fold
268      * is collapsed instead of the text contained in the fold.
269      * <br>
270      * If there is no specific description the "..." is returned.
271      *
272      * @return non-null description of the fold.
273      */

274     public String JavaDoc getDescription() {
275         return (description != null) ? description : DEFAULT_DESCRIPTION;
276     }
277     
278     void setDescription(String JavaDoc description) {
279         this.description = description;
280     }
281
282     /**
283      * Get total count of child folds contained in this fold.
284      *
285      * @return count of child folds contained in this fold.
286      * Zero means there are no children folds under this fold.
287      */

288     public int getFoldCount() {
289         return (children != null) ? children.getFoldCount() : 0;
290     }
291
292     /**
293      * Get child fold of this fold at the given index.
294      *
295      * @param index &gt;=0 &amp;&amp; &lt;{@link #getFoldCount()}
296      * index of child of this fold.
297      */

298     public Fold getFold(int index) {
299         if (children != null) {
300             return children.getFold(index);
301         } else { // no children exist
302
throw new IndexOutOfBoundsException JavaDoc("index=" + index // NOI18N
303
+ " but no children exist."); // NOI18N
304
}
305     }
306     
307     Fold[] foldsToArray(int index, int count) {
308         if (children != null) {
309             return children.foldsToArray(index, count);
310         } else { // no children
311
if (count == 0) {
312                 return EMPTY_FOLD_ARRAY;
313             } else { // invalid count
314
throw new IndexOutOfBoundsException JavaDoc("No children but count=" // NOI18N
315
+ count);
316             }
317         }
318     }
319
320     /**
321      * Remove the given folds and insert them as children
322      * of the given fold which will be put to their place.
323      *
324      * @param index index at which the starts the area of child folds to wrap.
325      * @param length number of child folds to wrap.
326      * @param fold fold to insert at place of children. The removed children
327      * become children of the fold.
328      */

329     void extractToChildren(int index, int length, Fold fold) {
330         if (fold.getFoldCount() != 0 || fold.getParent() != null) {
331             throw new IllegalStateException JavaDoc();
332         }
333         if (length != 0) { // create FoldChildren instance for the extracted folds
334
fold.setChildren(children.extractToChildren(index, length, fold));
335         } else { // no children to extract -> insert the single child
336
if (children == null) {
337                 children = new FoldChildren(this);
338             }
339             children.insert(index, fold); // insert the single child fold
340
}
341     }
342
343     /**
344      * Remove the fold at the given index
345      * and put its children at its place.
346      *
347      * @param index index at which the child should be removed
348      * @return the removed child at the index.
349      */

350     Fold replaceByChildren(int index) {
351         Fold fold = getFold(index);
352         FoldChildren foldChildren = fold.getChildren();
353         fold.setChildren(null);
354         children.replaceByChildren(index, foldChildren);
355         return fold;
356     }
357     
358     private FoldChildren getChildren() {
359         return children;
360     }
361     
362     private void setChildren(FoldChildren children) {
363         this.children = children;
364     }
365     
366     Object JavaDoc getExtraInfo() {
367         return extraInfo;
368     }
369
370     /**
371      * Get index of the given child fold in this fold.
372      * <br>
373      * The method has constant-time performance.
374      *
375      * @param child non-null child fold of this fold but in general
376      * it can be any non-null fold (see return value).
377      * @return &gt;=0 index of the child fold in this fold
378      * or -1 if the given child fold is not a child of this fold.
379      */

380     public int getFoldIndex(Fold child) {
381         return (children != null) ? children.getFoldIndex(child) : -1;
382     }
383     
384     private void updateGuardedStartPos(Document JavaDoc doc, int startOffset) throws BadLocationException JavaDoc {
385         if (!isRootFold()) {
386             int guardedStartOffset = isZeroStartGuardedLength()
387                 ? startOffset + 1
388                 : startOffset + startGuardedLength;
389             this.guardedStartPos = doc.createPosition(guardedStartOffset);
390         }
391     }
392
393     private void updateGuardedEndPos(Document JavaDoc doc, int endOffset) throws BadLocationException JavaDoc {
394         if (!isRootFold()) {
395             int guardedEndOffset = isZeroEndGuardedLength()
396                 ? endOffset - 1
397                 : endOffset - endGuardedLength;
398             this.guardedEndPos = doc.createPosition(guardedEndOffset);
399         }
400     }
401
402     private boolean isZeroStartGuardedLength() {
403         return (startGuardedLength == 0);
404     }
405     
406     private boolean isZeroEndGuardedLength() {
407         return (endGuardedLength == 0);
408     }
409     
410     private int getGuardedStartOffset() {
411         return isRootFold() ? getStartOffset() : guardedStartPos.getOffset();
412     }
413     
414     private int getGuardedEndOffset() {
415         return isRootFold() ? getEndOffset() : guardedEndPos.getOffset();
416     }
417     
418     void insertUpdate(DocumentEvent JavaDoc evt) {
419         if (evt.getOffset() + evt.getLength() == getGuardedStartOffset()) {
420              // inserted right at the end of the guarded area
421
try {
422                 updateGuardedStartPos(evt.getDocument(), getStartOffset());
423             } catch (BadLocationException JavaDoc e) {
424                 ErrorManager.getDefault().notify(e);
425             }
426         }
427     }
428     
429     void removeUpdate(DocumentEvent JavaDoc evt) {
430         try {
431             if (getStartOffset() == getGuardedStartOffset()) {
432                 updateGuardedStartPos(evt.getDocument(), getStartOffset());
433             }
434             if (getEndOffset() == getGuardedEndOffset()) {
435                 updateGuardedEndPos(evt.getDocument(), getEndOffset());
436             }
437         } catch (BadLocationException JavaDoc e) {
438             ErrorManager.getDefault().notify(e);
439         }
440     }
441     
442     /**
443      * Return true if the starting guarded area is damaged by a document modification.
444      */

445     boolean isStartDamaged() {
446         return (!isZeroStartGuardedLength() // no additional check if zero guarded length
447
&& (getInnerStartOffset() - getStartOffset() != startGuardedLength));
448     }
449     
450     /**
451      * Return true if the ending guarded area is damaged by a document modification.
452      */

453     boolean isEndDamaged() {
454         return (!isZeroEndGuardedLength() // no additional check if zero guarded length
455
&& (getEndOffset() - getInnerEndOffset() != endGuardedLength));
456     }
457     
458     boolean isExpandNecessary() {
459         // Only operate in case when isZero*() methods return true
460
// because if not the fold would already be marked as damaged
461
// and removed (isDamaged*() gets asked prior to this one).
462
return (isZeroStartGuardedLength() && (getStartOffset() == getGuardedStartOffset()))
463             || (isZeroEndGuardedLength() && (getEndOffset() == getGuardedEndOffset()));
464     }
465     
466     /**
467      * Get the position where the inner part of the fold starts
468      * (and the initial guarded area ends).
469      */

470     private int getInnerStartOffset() {
471         return isZeroStartGuardedLength() ? getStartOffset() : getGuardedStartOffset();
472     }
473
474     /**
475      * Get the position where the inner part of the fold ends
476      * (and the ending guarded area starts).
477      */

478     private int getInnerEndOffset() {
479         return isZeroEndGuardedLength() ? getEndOffset() : getGuardedEndOffset();
480     }
481
482     /**
483      * Get the raw index of this fold in the parent.
484      * <br>
485      * The SPI clients should never call this method.
486      */

487     int getRawIndex() {
488         return rawIndex;
489     }
490     
491     /**
492      * Set the raw index of this fold in the parent.
493      * <br>
494      * The SPI clients should never call this method.
495      */

496     void setRawIndex(int rawIndex) {
497         this.rawIndex = rawIndex;
498     }
499     
500     /**
501      * Update the raw index of this fold in the parent by a given delta.
502      * <br>
503      * The SPI clients should never call this method.
504      */

505     void updateRawIndex(int rawIndexDelta) {
506         this.rawIndex += rawIndexDelta;
507     }
508     
509
510     public String JavaDoc toString() {
511         return FoldUtilitiesImpl.foldToString(this) + ", [" + getInnerStartOffset() // NOI18N
512
+ ", " + getInnerEndOffset() + "] {" // NOI18N
513
+ getGuardedStartOffset() + ", " // NOI18N
514
+ getGuardedEndOffset() + '}'; // NOI18N
515
}
516     
517 }
518
Popular Tags