KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > fold > FoldHierarchyExecution


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.modules.editor.fold;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32 import javax.swing.SwingUtilities JavaDoc;
33 import javax.swing.event.DocumentEvent JavaDoc;
34 import javax.swing.event.DocumentListener JavaDoc;
35 import javax.swing.event.EventListenerList JavaDoc;
36 import javax.swing.text.AbstractDocument JavaDoc;
37 import javax.swing.text.BadLocationException JavaDoc;
38 import javax.swing.text.Document JavaDoc;
39 import javax.swing.text.JTextComponent JavaDoc;
40 import org.netbeans.api.editor.fold.Fold;
41 import org.netbeans.api.editor.fold.FoldHierarchy;
42 import org.netbeans.api.editor.fold.FoldHierarchyEvent;
43 import org.netbeans.api.editor.fold.FoldHierarchyListener;
44 import org.netbeans.api.editor.fold.FoldStateChange;
45 import org.netbeans.lib.editor.util.swing.DocumentListenerPriority;
46 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
47 import org.netbeans.spi.editor.fold.FoldManager;
48 import org.netbeans.spi.editor.fold.FoldManagerFactory;
49 import org.netbeans.spi.editor.fold.FoldOperation;
50 import org.netbeans.lib.editor.util.PriorityMutex;
51 import org.openide.ErrorManager;
52
53 /**
54  * Class backing the <code>FoldHierarchy</code> in one-to-one relationship.
55  * <br>
56  * The <code>FoldHierarchy</code> delegates all its operations
57  * to this object.
58  *
59  * <p>
60  * All the changes performed in to the folds are always done
61  * in terms of a transaction represented by {@link FoldHierarchyTransactionImpl}.
62  * The transaction can be opened by {@link #openTransaction()}.
63  *
64  * <p>
65  * This class changes its state upon displayability change
66  * of the associated component by listening on "ancestor" component property.
67  * <br>
68  * If the component is not displayable then the list of root folds becomes empty
69  * while if the component gets displayable the root folds are created
70  * according to registered managers.
71  *
72  * @author Miloslav Metelka
73  * @version 1.00
74  */

75
76 public final class FoldHierarchyExecution implements DocumentListener JavaDoc {
77     
78     private static final String JavaDoc PROPERTY_FOLD_HIERARCHY_MUTEX = "foldHierarchyMutex"; //NOI18N
79

80     private static final String JavaDoc PROPERTY_FOLDING_ENABLED = "code-folding-enable"; //NOI18N
81

82     private static final boolean debug
83         = Boolean.getBoolean("netbeans.debug.editor.fold"); //NOI18N
84

85     private static final boolean debugFire
86         = Boolean.getBoolean("netbeans.debug.editor.fold.fire"); //NOI18N
87

88     private static final FoldOperationImpl[] EMPTY_FOLD_OPERTAION_IMPL_ARRAY
89         = new FoldOperationImpl[0];
90     
91     static {
92         // The following call will make sure that the SpiPackageAccessor gets initialized
93
FoldOperation.isBoundsValid(0, 0, 0, 0);
94     }
95     
96     private final JTextComponent JavaDoc component;
97     
98     private FoldHierarchy hierarchy;
99     
100     private Fold rootFold;
101     
102     private FoldOperationImpl[] operations = EMPTY_FOLD_OPERTAION_IMPL_ARRAY;
103     
104     /**
105      * Map containing [blocked-fold, blocking-fold] pairs.
106      */

107     private Map JavaDoc blocked2block = new HashMap JavaDoc(4);
108     
109     /**
110      * Map containing [blocking-fold, blocked-fold-set] pairs.
111      */

112     private Map JavaDoc block2blockedSet = new HashMap JavaDoc(4);
113     
114     /** Whether hierarchy is initialized (root fold etc.) */
115     private boolean inited;
116     
117     private AbstractDocument JavaDoc lastDocument;
118     
119     private PriorityMutex mutex;
120     
121     private EventListenerList JavaDoc listenerList;
122     
123     private boolean foldingEnabled;
124     
125     private FoldHierarchyTransactionImpl activeTransaction;
126     
127     private PropertyChangeListener JavaDoc componentChangesListener;
128     
129     public static synchronized FoldHierarchy getOrCreateFoldHierarchy(JTextComponent JavaDoc component) {
130         if (component == null) {
131             throw new NullPointerException JavaDoc("component cannot be null"); // NOI18N
132
}
133
134         FoldHierarchyExecution execution
135             = (FoldHierarchyExecution)component.getClientProperty(FoldHierarchyExecution.class);
136         
137         if (execution == null) {
138             execution = new FoldHierarchyExecution(component);
139             execution.init();
140
141             component.putClientProperty(FoldHierarchyExecution.class, execution);
142         }
143         
144         return execution.getHierarchy();
145     }
146     
147     /**
148      * Construct new fold hierarchy SPI
149      *
150      * @param hierarchy hierarchy for which this SPI gets created.
151      * @param component comoponent for which this all happens.
152      */

153     private FoldHierarchyExecution(JTextComponent JavaDoc component) {
154         this.component = component;
155     }
156     
157     /**
158      * Initialize this spi by existing hierarchy instance
159      * (the one for which this spi was created).
160      * <br>
161      * This is called lazily upon first attempt to lock
162      * the hierarchy.
163      */

164     private void init() {
165         // Allow listeners to be added
166
listenerList = new EventListenerList JavaDoc();
167
168         // Assign mutex
169
mutex = (PriorityMutex)component.getClientProperty(PROPERTY_FOLD_HIERARCHY_MUTEX);
170         if (mutex == null) {
171             mutex = new PriorityMutex();
172             component.putClientProperty(PROPERTY_FOLD_HIERARCHY_MUTEX, mutex);
173         }
174
175         this.hierarchy = ApiPackageAccessor.get().createFoldHierarchy(this);
176         
177         Document JavaDoc doc = component.getDocument();
178         try {
179             rootFold = ApiPackageAccessor.get().createFold(
180                 new FoldOperationImpl(this, null, Integer.MAX_VALUE),
181                 FoldHierarchy.ROOT_FOLD_TYPE,
182                 "root", // NOI18N
183
false,
184                 doc,
185                 0, doc.getEndPosition().getOffset(),
186                 0, 0,
187                 null
188             );
189         } catch (BadLocationException JavaDoc e) {
190             ErrorManager.getDefault().notify(e);
191         }
192         
193         foldingEnabled = getFoldingEnabledSetting();
194
195         // Start listening on component changes
196
startComponentChangesListening();
197
198         rebuild();
199     }
200     
201     /**
202      * Get the fold hierarchy associated with this SPI
203      * in one-to-one relationship.
204      */

205     public final FoldHierarchy getHierarchy() {
206         return hierarchy;
207     }
208     
209     /**
210      * Lock the hierarchy for exclusive use. This method must only
211      * be used together with {@link #unlock()} in <code>try..finally</code> block.
212      * <br>
213      * Prior using this method the document must be locked.
214      * The document lock can be either readlock
215      * e.g. by using {@link javax.swing.text.Document#render(Runnable)}
216      * or writelock
217      * e.g. when in {@link javax.swing.event.DocumentListener})
218      * and must be obtained on component's document
219      * i.e. {@link javax.swing.text.JTextComponent#getDocument()}
220      * should be used.
221      *
222      * <p>
223      * The <code>FoldHierarchy</code> delegates to this method.
224      *
225      * <p>
226      * <font color="red">
227      * <b>Note:</b> The clients using this method must ensure that
228      * they <b>always</b> use this method in the following pattern:<pre>
229      *
230      * lock();
231      * try {
232      * ...
233      * } finally {
234      * unlock();
235      * }
236      * </pre>
237      * </font>
238      */

239     public final void lock() {
240         mutex.lock();
241     }
242     
243     /**
244      * Unlock the hierarchy from exclusive use. This method must only
245      * be used together with {@link #lock()} in <code>try..finally</code> block.
246      */

247     public void unlock() {
248         mutex.unlock();
249     }
250     
251     /**
252      * Get the text component for which this fold hierarchy was created.
253      * <br>
254      * The <code>FoldHierarchy</code> delegates to this method.
255      *
256      * @return non-null text component for which this fold hierarchy was created.
257      */

258     public JTextComponent JavaDoc getComponent() {
259         return component;
260     }
261
262     /**
263      * Get the root fold of this hierarchy.
264      * <br>
265      * The <code>FoldHierarchy</code> delegates to this method.
266      *
267      * @return root fold of this hierarchy.
268      * The root fold covers the whole document and is uncollapsable.
269      */

270     public Fold getRootFold() {
271         return rootFold;
272     }
273     
274     /**
275      * Add listener for changes done in the hierarchy.
276      * <br>
277      * The <code>FoldHierarchy</code> delegates to this method.
278      *
279      * @param l non-null listener to be added.
280      */

281     public void addFoldHierarchyListener(FoldHierarchyListener l) {
282         synchronized (listenerList) {
283             listenerList.add(FoldHierarchyListener.class, l);
284         }
285     }
286     
287     /**
288      * Remove previously added listener for changes done in the hierarchy.
289      * <br>
290      * The <code>FoldHierarchy</code> delegates to this method.
291      *
292      * @param l non-null listener to be removed.
293      */

294     public void removeFoldHierarchyListener(FoldHierarchyListener l) {
295         synchronized (listenerList) {
296             listenerList.remove(FoldHierarchyListener.class, l);
297         }
298     }
299     
300     void fireFoldHierarchyListener(FoldHierarchyEvent evt) {
301         if (debugFire) {
302             /*DEBUG*/System.err.println("Firing FoldHierarchyEvent:\n" + evt); // NOI18N
303
}
304
305         Object JavaDoc[] listeners = listenerList.getListenerList(); // no need to sync
306
for (int i = listeners.length - 2; i >= 0; i -= 2) {
307             if (listeners[i] == FoldHierarchyListener.class) {
308                 ((FoldHierarchyListener)listeners[i + 1]).foldHierarchyChanged(evt);
309             }
310         }
311         
312     }
313     
314     /**
315      * Attempt to add the given fold to the code folding hierarchy.
316      * The fold will either become part of the hierarchy or it will
317      * become blocked by another fold present in the hierarchy.
318      * <br>
319      * Only folds created by the fold operations of this hierarchy
320      * can be added.
321      *
322      * @param fold fold to be added
323      * @param transaction transaction under which the fold should be added.
324      * @return true if the fold was added successfully or false
325      * if it became blocked.
326      */

327     public boolean add(Fold fold, FoldHierarchyTransactionImpl transaction) {
328         if (fold.getParent() != null || isBlocked(fold)) {
329             throw new IllegalStateException JavaDoc("Fold already added: " + fold); // NOI18N
330
}
331         
332         boolean added = transaction.addFold(fold);
333         
334 // checkConsistency();
335

336         return added;
337     }
338     
339     /**
340      * Remove the fold that is either present in the hierarchy or blocked
341      * by another fold.
342      *
343      * @param fold fold to be removed
344      * @param transaction non-null transaction under which the fold should be removed.
345      */

346     public void remove(Fold fold, FoldHierarchyTransactionImpl transaction) {
347         transaction.removeFold(fold);
348         
349 // checkConsistency();
350
}
351     
352     /**
353      * Check whether the fold is currently present in the hierarchy or blocked.
354      *
355      * @return true if the fold is currently present in the hierarchy or blocked
356      * or false otherwise.
357      */

358     public boolean isAddedOrBlocked(Fold fold) {
359         return (fold.getParent() != null || isBlocked(fold));
360     }
361     
362     /**
363      * Is the given fold blocked by another fold?
364      */

365     public boolean isBlocked(Fold fold) {
366         return (getBlock(fold) != null);
367     }
368     
369     /**
370      * Get the fold blocking the given fold or null
371      * if the fold is not blocked.
372      */

373     Fold getBlock(Fold fold) {
374         return (blocked2block.size() > 0)
375             ? (Fold)blocked2block.get(fold)
376             : null;
377     }
378     
379     /**
380      * Mark given fold as blocked by the block fold.
381      */

382     void markBlocked(Fold blocked, Fold block) {
383         blocked2block.put(blocked, block);
384
385         Set JavaDoc blockedSet = (Set JavaDoc)block2blockedSet.get(block);
386         if (blockedSet == null) {
387             blockedSet = new HashSet JavaDoc();
388             block2blockedSet.put(block, blockedSet);
389         }
390         if (!blockedSet.add(blocked)) { // already added
391
throw new IllegalStateException JavaDoc("fold " + blocked + " already blocked"); // NOI18N
392
}
393     }
394     
395     /**
396      * Remove blocked fold from mappings.
397      *
398      * @param blocked fold
399      * @return fold that blocked the blocked fold.
400      * @throws IllegalArgumentException if the given blocked fold was not really blocked.
401      */

402     Fold unmarkBlocked(Fold blocked) {
403         // Find block for the given blocked fold
404
Fold block = (Fold)blocked2block.remove(blocked);
405         if (block == null) { // not blocked
406
throw new IllegalArgumentException JavaDoc("Not blocked: " + blocked); // NOI18N
407
}
408
409         // Remove the fold from set of blocked folds of the block
410
Set JavaDoc blockedSet = (Set JavaDoc)block2blockedSet.get(block);
411         if (!blockedSet.remove(blocked)) {
412             throw new IllegalStateException JavaDoc("Not blocker for " + blocked); // NOI18N
413
}
414         if (blockedSet.size() == 0) { // Remove the blocker as well
415
block2blockedSet.remove(block);
416         }
417         return block;
418     }
419
420     /**
421      * Mark the given block fold to be no longer blocking
422      * (and mark the folds blocked by the given block fold as not blocked).
423      *
424      * @param block the fold blocking others
425      * @return set of folds blocked by the block or null if the given fold
426      * was not block.
427      */

428     Set JavaDoc unmarkBlock(Fold block) {
429         Set JavaDoc blockedSet = (Set JavaDoc)block2blockedSet.remove(block);
430         if (blockedSet != null) {
431             // Remove all items of blocked set
432
int size = blocked2block.size();
433             blocked2block.keySet().removeAll(blockedSet);
434             if (size - blocked2block.size() != blockedSet.size()) { // not all removed
435
throw new IllegalStateException JavaDoc("Not all removed: " + blockedSet); // NOI18N
436
}
437         }
438         return blockedSet;
439     }
440
441     /**
442      * Collapse all folds in the given collection.
443      * <br>
444      * The <code>FoldHierarchy</code> delegates to this method.
445      */

446     public void collapse(Collection JavaDoc c) {
447         setCollapsed(c, true);
448     }
449     
450     /**
451      * Expand all folds in the given collection.
452      * <br>
453      * The <code>FoldHierarchy</code> delegates to this method.
454      */

455     public void expand(Collection JavaDoc c) {
456         setCollapsed(c, false);
457     }
458         
459     private void setCollapsed(Collection JavaDoc c, boolean collapsed) {
460         FoldHierarchyTransactionImpl transaction = openTransaction();
461         try {
462             for (Iterator JavaDoc it = c.iterator(); it.hasNext();) {
463                 Fold fold = (Fold)it.next();
464                 transaction.setCollapsed(fold, collapsed);
465             }
466         } finally {
467             transaction.commit();
468         }
469     }
470     
471     /**
472      * Open a new transaction on the fold hierarchy
473      * to make a change in the hierarchy.
474      * <br>
475      * Transaction is active until commited
476      * by calling <code>transaction.commit()</code>.
477      * <br>
478      * Only one transaction can be active at the time.
479      * <br>
480      * There is currently no way to rollback the transaction.
481      *
482      * <p>
483      * <font color="red">
484      * <b>Note:</b> The clients using this method must ensure that
485      * they <b>always</b> use this method in the following pattern:<pre>
486      *
487      * FoldHierarchyTransaction transaction = operation.openTransaction();
488      * try {
489      * ...
490      * } finally {
491      * transaction.commit();
492      * }
493      * </pre>
494      * </font>
495
496      */

497     public FoldHierarchyTransactionImpl openTransaction() {
498         if (activeTransaction != null) {
499             throw new IllegalStateException JavaDoc("Active transaction already exists."); // NOI18N
500
}
501         activeTransaction = new FoldHierarchyTransactionImpl(this);
502         return activeTransaction;
503     }
504     
505     void clearActiveTransaction() {
506         if (activeTransaction == null) {
507             throw new IllegalStateException JavaDoc("No transaction in progress"); // NOI18N
508
}
509         activeTransaction = null;
510     }
511
512     void createAndFireFoldHierarchyEvent(
513     Fold[] removedFolds, Fold[] addedFolds,
514     FoldStateChange[] foldStateChanges,
515     int affectedStartOffset, int affectedEndOffset) {
516         
517         // Check correctness
518
if (affectedStartOffset < 0) {
519             throw new IllegalArgumentException JavaDoc("affectedStartOffset=" // NOI18N
520
+ affectedStartOffset + " < 0"); // NOI18N
521
}
522         
523         if (affectedEndOffset < affectedStartOffset) {
524             throw new IllegalArgumentException JavaDoc("affectedEndOffset=" // NOI18N
525
+ affectedEndOffset + " < affectedStartOffset=" + affectedStartOffset); // NOI18N
526
}
527
528         FoldHierarchyEvent evt = ApiPackageAccessor.get().createFoldHierarchyEvent(
529             hierarchy,
530             removedFolds, addedFolds, foldStateChanges,
531             affectedStartOffset, affectedEndOffset
532         );
533
534         fireFoldHierarchyListener(evt);
535     }
536
537     /**
538      * Rebuild the fold hierarchy - the fold managers will be recreated.
539      */

540     public void rebuild() {
541         // Stop listening on the original document
542
if (lastDocument != null) {
543             // Remove document listener with specific priority
544
DocumentUtilities.removeDocumentListener(lastDocument, this, DocumentListenerPriority.FOLD_UPDATE);
545             lastDocument = null;
546         }
547
548         Document JavaDoc doc = getComponent().getDocument();
549         AbstractDocument JavaDoc adoc;
550         boolean releaseOnly; // only release the current hierarchy root folds
551
if (doc instanceof AbstractDocument JavaDoc) {
552             adoc = (AbstractDocument JavaDoc)doc;
553             releaseOnly = false;
554         } else { // doc is null or non-AbstractDocument => release the hierarchy
555
adoc = null;
556             releaseOnly = true;
557         }
558         
559         if (!foldingEnabled) { // folding not enabled => release
560
releaseOnly = true;
561         }
562         
563         if (adoc != null) {
564             adoc.readLock();
565             
566             // Start listening for changes
567
if (!releaseOnly) {
568                 lastDocument = adoc;
569                 // Add document listener with specific priority
570
DocumentUtilities.addDocumentListener(lastDocument, this, DocumentListenerPriority.FOLD_UPDATE);
571             }
572         }
573         try {
574             lock();
575             try {
576                 rebuildManagers(releaseOnly);
577             } finally {
578                 unlock();
579             }
580         } finally {
581             if (adoc != null) {
582                 adoc.readUnlock();
583             }
584         }
585     }
586         
587     /**
588      * Rebuild (or release) the root folds of the hierarchy in the event dispatch thread.
589      *
590      * @param releaseOnly release the current root folds
591      * but make the new root folds array empty.
592      */

593     private void rebuildManagers(boolean releaseOnly) {
594         for (int i = 0; i < operations.length; i++) {
595             operations[i].release();
596         }
597         operations = EMPTY_FOLD_OPERTAION_IMPL_ARRAY; // really release
598

599         // Call all the providers
600
FoldManagerFactoryProvider provider = !releaseOnly
601             ? FoldManagerFactoryProvider.getDefault()
602             : FoldManagerFactoryProvider.getEmpty();
603
604         List JavaDoc factoryList = provider.getFactoryList(getHierarchy());
605         int factoryListLength = factoryList.size();
606
607         if (debug) {
608             /*DEBUG*/System.err.println("FoldHierarchy rebuild():" // NOI18N
609
+ " FoldManager factory count=" + factoryListLength // NOI18N
610
);
611         }
612         
613         // Create fold managers
614
int priority = factoryListLength - 1; // highest priority (till lowest == 0)
615
boolean ok = false;
616         try {
617             operations = new FoldOperationImpl[factoryListLength];
618             for (int i = 0; i < factoryListLength; i++) {
619                 FoldManagerFactory factory = (FoldManagerFactory)factoryList.get(i);
620                 FoldManager manager = factory.createFoldManager();
621                 operations[i] = new FoldOperationImpl(this, manager, priority);
622                 priority--;
623             }
624             ok = true;
625         } finally {
626             if (!ok) {
627                 operations = EMPTY_FOLD_OPERTAION_IMPL_ARRAY;
628             }
629         }
630
631         // Init managers under a local transaction
632
FoldHierarchyTransactionImpl transaction = openTransaction();
633         ok = false;
634         try {
635             // Remove all original folds - pass array of all blocked folds
636
Fold[] allBlocked = new Fold[blocked2block.size()];
637             blocked2block.keySet().toArray(allBlocked);
638             transaction.removeAllFolds(allBlocked);
639
640             // Init folds in all fold managers
641
// Go from the manager with highest priority (index 0)
642
for (int i = 0; i < factoryListLength; i++) {
643                 operations[i].initFolds(transaction);
644             }
645             ok = true; // inited successfully
646
} finally {
647             if (!ok) {
648                 // TODO - remove folds under root fold
649
operations = EMPTY_FOLD_OPERTAION_IMPL_ARRAY;
650             }
651             transaction.commit();
652         }
653     }
654     
655     private void startComponentChangesListening() {
656         if (componentChangesListener == null) {
657             // Start listening on component changes
658
componentChangesListener = new PropertyChangeListener JavaDoc() {
659                 public void propertyChange(PropertyChangeEvent JavaDoc evt) {
660                     String JavaDoc propName = evt.getPropertyName();
661                     if ("document".equals(propName)) { //NOI18N
662
rebuild();
663                     } else if (PROPERTY_FOLDING_ENABLED.equals(propName)) {
664                         foldingEnabledSettingChange();
665                     }
666                 }
667             };
668
669             // Start listening on the component.
670
// As the hierarchy instance is stored as a property of the component
671
// (and in fact the spi and the reference to the listener as well)
672
// the listener does not need to be removed
673
getComponent().addPropertyChangeListener(componentChangesListener);
674         }
675     }
676     
677     public void insertUpdate(DocumentEvent JavaDoc evt) {
678         lock();
679         try {
680             FoldHierarchyTransactionImpl transaction = openTransaction();
681             try {
682                 transaction.insertUpdate(evt);
683
684                 int operationsLength = operations.length;
685                 for (int i = 0; i < operationsLength; i++) {
686                     operations[i].insertUpdate(evt, transaction);
687                 }
688             } finally {
689                 transaction.commit();
690             }
691         } finally {
692             unlock();
693         }
694     }
695
696     public void removeUpdate(DocumentEvent JavaDoc evt) {
697         lock();
698         try {
699             FoldHierarchyTransactionImpl transaction = openTransaction();
700             try {
701                 transaction.removeUpdate(evt);
702
703                 int operationsLength = operations.length;
704                 for (int i = 0; i < operationsLength; i++) {
705                     operations[i].removeUpdate(evt, transaction);
706                 }
707             } finally {
708                 transaction.commit();
709             }
710         } finally {
711             unlock();
712         }
713     }
714
715     public void changedUpdate(DocumentEvent JavaDoc evt) {
716         lock();
717         try {
718             FoldHierarchyTransactionImpl transaction = openTransaction();
719             try {
720                 transaction.changedUpdate(evt);
721                 
722                 int operationsLength = operations.length;
723                 for (int i = 0; i < operationsLength; i++) {
724                     operations[i].changedUpdate(evt, transaction);
725                 }
726             } finally {
727                 transaction.commit();
728             }
729         } finally {
730             unlock();
731         }
732     }
733
734     private boolean getFoldingEnabledSetting() {
735         Boolean JavaDoc b = (Boolean JavaDoc)component.getClientProperty(PROPERTY_FOLDING_ENABLED);
736         return (b != null) ? b.booleanValue() : true;
737     }
738     
739     public void foldingEnabledSettingChange() {
740         boolean origFoldingEnabled = foldingEnabled;
741         foldingEnabled = getFoldingEnabledSetting();
742         if (origFoldingEnabled != foldingEnabled) {
743             SwingUtilities.invokeLater(new Runnable JavaDoc() {
744                 public void run() {
745                     lock();
746                     try{
747                         rebuild();
748                     } finally {
749                         unlock();
750                     }
751                 }
752             });
753         }
754     }
755     
756     /**
757      * Check the internal consistency of the hierarchy
758      * and its contained folds. This is useful for testing purposes.
759      *
760      * @throws IllegalStateException in case an inconsistency is found.
761      */

762     public void checkConsistency() {
763         try {
764             checkFoldConsistency(getRootFold());
765         } catch (RuntimeException JavaDoc e) {
766             /*DEBUG*/System.err.println("FOLD HIERARCHY INCONSISTENCY FOUND\n" + this); // NOI18N
767
throw e; // rethrow the exception
768
}
769     }
770     
771     private static void checkFoldConsistency(Fold fold) {
772         int startOffset = fold.getStartOffset();
773         int endOffset = fold.getEndOffset();
774         int lastEndOffset = startOffset;
775         
776         for (int i = 0; i < fold.getFoldCount(); i++) {
777             Fold child = fold.getFold(i);
778             if (child.getParent() != fold) {
779                 throw new IllegalStateException JavaDoc("Wrong parent of child=" // NOI18N
780
+ child + ": " + child.getParent() // NOI18N
781
+ " != " + fold); // NOI18N
782
}
783             int foldIndex = fold.getFoldIndex(child);
784             if (foldIndex != i) {
785                 throw new IllegalStateException JavaDoc("Fold index " + foldIndex // NOI18N
786
+ " instead of " + i); // NOI18N
787
}
788             
789             int childStartOffset = child.getStartOffset();
790             int childEndOffset = child.getEndOffset();
791             if (childStartOffset < lastEndOffset) {
792                 throw new IllegalStateException JavaDoc("childStartOffset=" + childStartOffset // NOI18N
793
+ " < lastEndOffset=" + lastEndOffset); // NOI18N
794
}
795             if (childStartOffset > childEndOffset) {
796                 throw new IllegalStateException JavaDoc("childStartOffset=" + childStartOffset // NOI18N
797
+ " > childEndOffset=" + childEndOffset); // NOI18N
798
}
799             lastEndOffset = childEndOffset;
800             
801             checkFoldConsistency(child);
802         }
803     }
804     
805     public String JavaDoc toString() {
806         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
807         sb.append("component="); // NOI18N
808
sb.append(System.identityHashCode(getComponent()));
809         sb.append('\n');
810
811         // Append info about root folds
812
sb.append(FoldUtilitiesImpl.foldToStringChildren(hierarchy.getRootFold(), 0));
813         sb.append('\n');
814         
815         // Append info about blocked folds
816
if (blocked2block != null) {
817             sb.append("BLOCKED\n"); // NOI18N
818
for (Iterator JavaDoc it = blocked2block.entrySet().iterator(); it.hasNext();) {
819                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
820                 sb.append(" "); // NOI18N
821
sb.append(entry.getKey());
822                 sb.append(" blocked-by "); // NOI18N
823
sb.append(entry.getValue());
824                 sb.append('\n');
825             }
826         }
827         
828         // Append info about blockers
829
if (block2blockedSet != null) {
830             sb.append("BLOCKERS\n"); // NOI18N
831
for (Iterator JavaDoc it = block2blockedSet.entrySet().iterator(); it.hasNext();) {
832                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
833                 sb.append(" "); // NOI18N
834
sb.append(entry.getKey());
835                 sb.append('\n');
836                 Set JavaDoc blockedSet = (Set JavaDoc)entry.getValue();
837                 for (Iterator JavaDoc it2 = blockedSet.iterator(); it2.hasNext();) {
838                     sb.append(" blocks "); // NOI18N
839
sb.append(it2.next());
840                     sb.append('\n');
841                 }
842             }
843         }
844         
845         int operationsLength = operations.length;
846         if (operationsLength > 0) {
847             sb.append("Fold Managers\n"); // NOI18N
848
for (int i = 0; i < operationsLength; i++) {
849                 sb.append("FOLD MANAGER ["); // NOI18N
850
sb.append(i);
851                 sb.append("]:\n"); // NOI18N
852
sb.append(operations[i].getManager());
853                 sb.append("\n"); // NOI18N
854
}
855         }
856         
857         return sb.toString();
858     }
859
860 }
861
Popular Tags