KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > JavaEditor


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.java;
21
22 import java.awt.Component JavaDoc;
23 import java.awt.event.ActionEvent JavaDoc;
24 import java.awt.event.ActionListener JavaDoc;
25 import java.beans.PropertyChangeEvent JavaDoc;
26 import java.beans.PropertyChangeListener JavaDoc;
27 import java.beans.PropertyVetoException JavaDoc;
28 import java.io.*;
29 import java.lang.ref.Reference JavaDoc;
30 import java.lang.ref.ReferenceQueue JavaDoc;
31 import java.lang.ref.WeakReference JavaDoc;
32 import java.nio.CharBuffer JavaDoc;
33 import java.util.*;
34 import java.util.regex.Matcher JavaDoc;
35 import java.util.regex.Pattern JavaDoc;
36 import javax.swing.*;
37 import javax.swing.Timer JavaDoc;
38 import javax.swing.event.*;
39 import javax.jmi.reflect.JmiException;
40 import javax.swing.text.BadLocationException JavaDoc;
41 import javax.swing.text.Caret JavaDoc;
42 import javax.swing.text.Document JavaDoc;
43 import javax.swing.text.EditorKit JavaDoc;
44 import javax.swing.text.Position JavaDoc;
45 import javax.swing.text.StyledDocument JavaDoc;
46 import org.netbeans.api.java.classpath.ClassPath;
47 import org.netbeans.jmi.javamodel.*;
48 import org.netbeans.modules.java.settings.JavaSettings;
49 import org.netbeans.modules.java.ui.nodes.SourceNodeFactory;
50 import org.netbeans.modules.java.ui.nodes.SourceNodes;
51 import org.netbeans.modules.javacore.JMManager;
52 import org.netbeans.modules.javacore.RepositoryUpdater;
53 import org.netbeans.modules.javacore.RequestPoster;
54 import org.netbeans.modules.javacore.api.JavaModel;
55 import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
56 import org.netbeans.modules.javacore.internalapi.ParsingListener;
57 import org.netbeans.modules.javacore.internalapi.UndoManager;
58 import org.netbeans.modules.javacore.jmiimpl.javamodel.ResourceImpl;
59 import org.openide.DialogDisplayer;
60 import org.openide.NotifyDescriptor;
61 import org.openide.awt.UndoRedo;
62 import org.openide.cookies.CloseCookie;
63 import org.openide.cookies.EditorCookie;
64 import org.openide.cookies.LineCookie;
65 import org.openide.cookies.OpenCookie;
66 import org.openide.cookies.PrintCookie;
67 import org.openide.cookies.SaveCookie;
68 import org.openide.filesystems.FileLock;
69 import org.openide.filesystems.FileObject;
70 import org.openide.loaders.DataObject;
71 import org.openide.nodes.Node;
72 import org.openide.nodes.Children;
73 import org.openide.src.SourceElement;
74 import org.openide.util.actions.SystemAction;
75 import org.openide.ErrorManager;
76 import org.openide.text.*;
77 import org.openide.text.Annotation;
78 import org.openide.util.Mutex;
79 import org.openide.util.NbBundle;
80 import org.openide.util.RequestProcessor;
81 import org.openide.util.Utilities;
82 import org.openide.windows.CloneableOpenSupport;
83
84
85 /*
86 * TODO:
87 * 1) undo support
88 */

89 /** Java source-file extension for handling the Editor.
90 * The main purpose of this class is to manage guarded sections.
91 *
92 *
93 * @author Petr Hamernik
94 */

95 public class JavaEditor extends DataEditorSupport implements PropertyChangeListener JavaDoc, Node.Cookie, OpenCookie, EditorCookie.Observable, CloseCookie, PrintCookie {
96     /** The prefix of all magic strings */
97     final static String JavaDoc MAGIC_PREFIX = "//GEN-"; // NOI18N
98

99     /** Magic strings - special comments which are inserted during saving
100      * and are removed during loading.
101      */

102     private static String JavaDoc[] SECTION_MAGICS;
103     private static final int LONGEST_ITEM = 10;
104
105     private boolean shouldReload = false;
106     private boolean wasReloaded = false;
107
108     static {
109
110         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(MAGIC_PREFIX);
111         int size = sb.length();
112
113         SECTION_MAGICS = new String JavaDoc[7];
114         SECTION_MAGICS[0] = sb.append("LINE:").toString(); // NOI18N
115
sb.setLength(size);
116         SECTION_MAGICS[1] = sb.append("BEGIN:").toString(); // NOI18N
117
sb.setLength(size);
118         SECTION_MAGICS[2] = sb.append("END:").toString(); // NOI18N
119
sb.setLength(size);
120         SECTION_MAGICS[3] = sb.append("HEADER:").toString(); // NOI18N
121
sb.setLength(size);
122         SECTION_MAGICS[4] = sb.append("HEADEREND:").toString(); // NOI18N
123
sb.setLength(size);
124         SECTION_MAGICS[5] = sb.append("FIRST:").toString(); // NOI18N
125
sb.setLength(size);
126         SECTION_MAGICS[6] = sb.append("LAST:").toString(); // NOI18N
127
}
128
129     /** Types of the magic comments. */
130     private final static int T_LINE = 0;
131     private final static int T_BEGIN = 1;
132     private final static int T_END = 2;
133     private final static int T_HEADER = 3;
134     private final static int T_HEADEREND = 4;
135     // obsoleted - only for backward compatibility
136
private final static int T_FIRST = 5;
137     private final static int T_LAST = 6;
138
139     /** Table of the guarded sections. Keys are the names of the sections
140     * and values are the GuardedSection classes. The table is null till
141     * while document is not in the memory.
142     */

143     HashMap sections = null;
144
145     /** Timer which countdowns the auto-reparsing time. */
146     Timer JavaDoc timer;
147
148     /** New lines in this file was delimited by '\n' */
149     static final byte NEW_LINE_N = 0;
150
151     /** New lines in this file was delimited by '\r' */
152     static final byte NEW_LINE_R = 1;
153
154     /** New lines in this file was delimited by '\r\n' */
155     static final byte NEW_LINE_RN = 2;
156
157     /** The type of new lines */
158     byte newLineType;
159
160     private transient boolean hasAnnotations = false;
161
162     /**
163      * Helper variable for document reloading support. If the loadFromStream is called
164      * and reloading is true, source reparsing is forced.
165      */

166     private boolean reloading = false;
167
168     private static final Comparator SECTION_COMPARATOR = new GuardedPositionComparator();
169
170     /* List of error annotations attached to this document */
171     private ArrayList errorAnnotations=new ArrayList();
172
173     /** queue processing error annotations */
174     private static final RequestProcessor ERROR_ANNOTATION_QUEUE =
175             new RequestProcessor("Error Annotation Queue", 1); // NOI18N
176

177     private boolean parsingAttached;
178
179     private ParsingListener wParsingL;
180     
181     private OverrideAnnotationSupport overriddensSupport;
182
183     /** Classpaths associated with this file.
184      * Never use this fields directly, may not be initialized.
185      * Use the corresponging gethers. */

186     private Reference JavaDoc/*<ClassPath>*/ sourceClasspath;
187     private Reference JavaDoc/*<ClassPath>*/ librariesClasspath;
188     private Reference JavaDoc/*<ClassPath>*/ bootClasspath;
189
190     private UndoRedo.Manager undoRedo = null;
191     private boolean undoRedoPrecreated = false;
192
193     private transient String JavaDoc resourceMofId = null;
194     private transient WeakReference JavaDoc resource = null;
195
196     private final ParsingListener listener = new ParsingListener() {
197         public void resourceParsed(final Resource resource) {
198             JavaMetamodel.getDefaultRepository().beginTrans(false);
199             try {
200                 if (resource == getResource()) {
201                     notifyParsingDone();
202                 }
203             } finally {
204                 JavaMetamodel.getDefaultRepository().endTrans();
205             }
206         }
207     };
208
209     /** Create a new Editor support for the given Java source.
210     * @param entry the (primary) file entry representing the Java source file
211     */

212     public JavaEditor(DataObject dob) {
213         super(dob, new JavaEditorEnv(dob));
214         // add change listener
215
addChangeListener(new JavaEditorChangeListener());
216         JavaMetamodel.getUndoManager().addPropertyChangeListener(new UndoManagerListener(this));
217     }
218
219     private void changeTimeoutElapsed() {
220         parseSource(false, true);
221     }
222     
223     protected boolean notifyModified() {
224         if (! super.notifyModified()) {
225             return false;
226         }
227         JavaDataObject obj = (JavaDataObject) getDataObject();
228         if (obj.getCookie(SaveCookie.class) == null) {
229             obj.addSaveCookie(new Save());
230         }
231         return true;
232     }
233     
234     protected void notifyUnmodified() {
235         super.notifyUnmodified();
236         JavaDataObject obj = (JavaDataObject) getDataObject();
237         SaveCookie save = (SaveCookie) obj.getCookie(SaveCookie.class);
238         if (save != null) {
239             obj.removeSaveCookie(save);
240         }
241     }
242     
243     private class Save implements SaveCookie {
244         public void save() throws IOException {
245             saveDocument();
246             if (wasReloaded) {
247                 // simple hack because of issue #79363
248
getDataObject().setModified(true);
249                 wasReloaded = false;
250             } else {
251                 getDataObject().setModified(false);
252             }
253         }
254     }
255     
256
257     private void parseSource(final boolean force, final boolean refreshAnnotations) {
258         ERROR_ANNOTATION_QUEUE.post(new Runnable JavaDoc() {
259             public void run() {
260                 if (force) {
261                     JavaMetamodel.getManager().addModified(getDataObject().getPrimaryFile());
262                 } else if (forceParseOnComponentActivated) {
263                     ResourceImpl resource = (ResourceImpl) getResource();
264                     if (resource != null) resource.resetErrors();
265                 }
266                 Document JavaDoc doc = getDocument();
267                 
268                 if (doc != null)
269                     JavaUpToDateStatusProvider.get(doc).notifyParsingStarted();
270                 
271                 //beginTrans(writeAccess=true) cause reparse of all registered data objects
272
JavaMetamodel.getDefaultRepository().beginTrans(true);
273                 JavaMetamodel.getDefaultRepository().endTrans(false);
274                 if (refreshAnnotations)
275                     refreshAnnotations();
276                 
277                 if (doc != null)
278                     JavaUpToDateStatusProvider.get(doc).notifyParsingFinished();
279             }
280         });
281     }
282
283     private void classpathChanged() {
284         parseSource(true, true);
285     }
286
287     private void parsingErrorsChanged(PropertyChangeEvent JavaDoc evt) {
288         int errors=JavaSettings.getDefault().getParsingErrors();
289         Integer JavaDoc old=(Integer JavaDoc)evt.getOldValue();
290         int oldErrors=JavaSettings.DEFAULT_PARSING_ERRORS;
291
292         if (old!=null) {
293             oldErrors=old.intValue();
294         }
295         if (oldErrors==errors) // no change
296
return;
297         if (errors==0 && !errorAnnotations.isEmpty()) { // dettach all annotations
298
detachAnnotations(errorAnnotations);
299             errorAnnotations.clear();
300             return;
301         }
302         if (oldErrors==errorAnnotations.size() || errors<errorAnnotations.size()) { // display error annotations
303
refreshAnnotations();
304         }
305     }
306
307
308     private void showOverridingChanged (PropertyChangeEvent JavaDoc event) {
309         if (getOpenedPanes() == null) return;
310         
311         boolean newValue = JavaSettings.getDefault().getShowOverriding();
312         if (newValue) {
313             //Initial building of override annotations after change of settings
314
overriddensSupport.processOverriddenAnnotation();
315         } else {
316             synchronized (JavaEditor.this) {
317                 overriddensSupport.suspend();
318                 overriddensSupport = new OverrideAnnotationSupport(JavaEditor.this);
319             }
320         }
321     }
322
323     private synchronized void attachParsingListener() {
324         if (!parsingAttached) {
325             if (wParsingL == null)
326                 wParsingL = new WParsingListener(listener);
327             JavaMetamodel.addParsingListener(wParsingL);
328             parsingAttached = true;
329         }
330         if (overriddensSupport == null) {
331             overriddensSupport = new OverrideAnnotationSupport(this);
332         }
333     }
334
335     private synchronized void removeParsingListener() {
336     if (parsingAttached) {
337             JavaMetamodel.removeParsingListener(wParsingL);
338             parsingAttached=false;
339         }
340     }
341
342     /** Restart the timer which starts the parser after the specified delay.
343     * @param onlyIfRunning Restarts the timer only if it is already running
344     */

345     void restartTimer(boolean onlyIfRunning) {
346         restartTimer(onlyIfRunning, false);
347     }
348     
349     private boolean forceParseOnComponentActivated = false;
350     
351     private void restartTimer(boolean onlyIfRunning, boolean componentActivated) {
352         int delay;
353         boolean timerRunning = timer!=null && timer.isRunning();
354         
355         if (onlyIfRunning && !timerRunning)
356             return;
357
358         if (!onlyIfRunning)
359             forceParseOnComponentActivated = componentActivated;
360         delay = JavaSettings.getDefault().getAutoParsingDelay();
361         if (delay<=0)
362             return;
363         if (timer==null) { // initialize timer
364
timer = new Timer JavaDoc(0, new ActionListener JavaDoc() {
365                               public void actionPerformed(ActionEvent JavaDoc e) {
366                                   changeTimeoutElapsed();
367                               }
368                           });
369             timer.setRepeats(false);
370         }
371         timer.setInitialDelay(delay);
372         timer.restart();
373     }
374
375     private void stopTimerIfPossible() {
376         if (forceParseOnComponentActivated && timer!=null) {
377             timer.stop();
378         }
379     }
380
381
382     public void openAtPosition(PositionRef begin) {
383         openAt(begin, -1).getComponent().requestActive();
384     }
385     
386     public void openAt(PositionRef p) {
387         openAtPosition(p);
388     }
389
390     /** Notify about the editor closing.
391     */

392     protected void notifyClosed() {
393         synchronized (this) {
394             removeParsingListener();
395             if (overriddensSupport != null) overriddensSupport.suspend();
396             overriddensSupport = null;
397             hasAnnotations = false;
398         }
399         boolean wasModified = this.isModified();
400         
401         // HACK !!! This synchronization exploits implementation details of CloneableEditorSupport -
402
// but it is more or less the cleanest way
403
synchronized (allEditors) {
404             super.notifyClosed();
405             clearSections();
406         }
407         
408         // in case that changes of the document has been discarded refresh the source hierarchy
409
if (wasModified) {
410             parseSource(true, false);
411         }
412     }
413
414     /** Notify that parsing task has been finished; some dependent data may now
415       be refreshed from up-to-date parsing info
416     */

417     protected void notifyParsingDone() {
418     }
419     
420     private void refreshAnnotations() {
421         // force errors construction
422
((JMManager) JMManager.getManager()).waitScanFinished();
423         if (JMManager.PERF_DEBUG) Thread.dumpStack();
424         Resource r = getResource();
425         if (r != null) {
426             try {
427                 final List errors = r.getErrors();
428                 // force parsing for errors out of AWT thread
429
errors.size();
430                 SwingUtilities.invokeLater(new Runnable JavaDoc() {
431                     public void run() {
432                         processAnnotations(errors);
433                     }
434                 });
435             } catch (javax.jmi.reflect.InvalidObjectException e) {
436                 // ignore
437
// (allow the exception and ignore it rather than doing whole method in a transaction, so that the AWT thread is not blocked)
438
}
439         }
440     }
441
442     public Resource getResource() {
443         Resource result = resource == null ? null : (Resource) resource.get();
444         if (result != null) {
445             try {
446                 result.refImmediateComposite();
447             } catch (javax.jmi.reflect.InvalidObjectException e) {
448                 resourceMofId = null;
449                 result = null;
450             }
451         }
452         if (result == null || !result.isValid()) {
453             if (resourceMofId != null) {
454                 result = (Resource) JavaMetamodel.getDefaultRepository().getByMofId(resourceMofId);
455             }
456             if (result == null || !result.isValid()) {
457                 result = JavaMetamodel.getManager().getResource(getDataObject().getPrimaryFile());
458                 if (result==null)
459                     return null;
460                 resourceMofId = result.refMofId();
461             }
462             resource = new WeakReference JavaDoc(result);
463         }
464         return result;
465     }
466
467     /** Read the file from the stream, filter the guarded section
468     * comments, and mark the sections in the editor.
469     *
470     * @param doc the document to read into
471     * @param stream the open stream to read from
472     * @param kit the associated editor kit
473     * @throws IOException if there was a problem reading the file
474     * @throws BadLocationException should not normally be thrown
475     * @see #saveFromKitToStream
476     */

477     protected void loadFromStreamToKit (StyledDocument JavaDoc doc, InputStream stream, EditorKit JavaDoc kit) throws IOException, BadLocationException JavaDoc {
478         sections = new HashMap(10);
479         GuardedReader reader = new GuardedReader(stream, false,
480             Util.getFileEncoding(getDataObject().getPrimaryFile()));
481         kit.read(reader, doc, 0);
482         fillSections(reader, doc);
483         newLineType = reader.getNewLineType();
484         // stream is not closed, because is it close outside this method
485

486         // parse it and remember the hook to prevent to garbage collected
487
// parsed hierarchy
488
final boolean forceUpdate = reloading;
489
490         reloading = false;
491         if (forceUpdate) {
492             ERROR_ANNOTATION_QUEUE.post(new Runnable JavaDoc() {
493                 public void run() {
494                     refreshAnnotations();
495                 }
496             });
497         }
498     }
499
500     /** Store the document and add the special comments signifying
501     * guarded sections.
502     *
503     * @param doc the document to write from
504     * @param kit the associated editor kit
505     * @param stream the open stream to write to
506     * @throws IOException if there was a problem writing the file
507     * @throws BadLocationException should not normally be thrown
508     * @see #loadFromStreamToKit
509     */

510     protected void saveFromKitToStream(StyledDocument JavaDoc doc, EditorKit JavaDoc kit, OutputStream stream) throws IOException, BadLocationException JavaDoc {
511         OutputStream os = new NewLineOutputStream(stream, newLineType);
512         String JavaDoc encoding = Util.getFileEncoding(getDataObject().getPrimaryFile());
513         if (sections != null) {
514             ArrayList list = new ArrayList(sections.values());
515             if (list.size() > 0) {
516                 GuardedWriter writer = new GuardedWriter(os, list, encoding);
517                 kit.write(writer, doc, 0, doc.getLength());
518                 return;
519             }
520         }
521         Writer w;
522         if (encoding == null)
523             w = new OutputStreamWriter(os);
524         else
525             w = new OutputStreamWriter(os, encoding);
526         kit.write(w, doc, 0, doc.getLength());
527     }
528
529     /** Save the document in this thread and start reparsing it.
530     * @exception IOException on I/O error
531     */

532     public void saveDocument () throws IOException {
533         saveDocument(true);
534         if (shouldReload) {
535             reloadDocument();
536             shouldReload = false;
537             wasReloaded = true;
538         }
539     }
540
541     /** Save the document in this thread.
542     * @param forceSave if true save always, otherwise only when is modified
543     * @exception IOException on I/O error
544     */

545     private void saveDocument(boolean forceSave) throws IOException {
546         if (forceSave || isModified()) {
547             if (!checkCharsetConversion(Util.getFileEncoding(getDataObject().getPrimaryFile()))){
548                 return;
549             }
550             RepositoryUpdater.getDefault().addFileObjectToSave(getDataObject().getPrimaryFile());
551             super.saveDocument();
552         }
553     }
554     
555     private boolean checkCharsetConversion(String JavaDoc encoding) {
556         if (encoding == null)
557             return true;
558         boolean value = true;
559         try {
560             java.nio.charset.CharsetEncoder JavaDoc coder = java.nio.charset.Charset.forName(encoding).newEncoder();
561             if (!coder.canEncode(getDocument().getText(0, getDocument().getLength()))){
562                 NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
563                     NbBundle.getMessage(JavaEditor.class, "MSG_BadCharConversion", //NOI18N
564
new Object JavaDoc [] { getDataObject().getPrimaryFile().getNameExt(),
565                     encoding}),
566                     NotifyDescriptor.YES_NO_OPTION,
567                     NotifyDescriptor.WARNING_MESSAGE);
568                     nd.setValue(NotifyDescriptor.NO_OPTION);
569                     DialogDisplayer.getDefault().notify(nd);
570                 if(nd.getValue() != NotifyDescriptor.YES_OPTION)
571                     value = false;
572             }
573         }
574         catch (javax.swing.text.BadLocationException JavaDoc e){
575             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
576         }
577         return value;
578     }
579
580     private void processAnnotations(List errors) {
581         ArrayList added,removed,unchanged;
582         Collection newAnnotations;
583         newAnnotations = getAnnotations(errors);
584         added=new ArrayList(newAnnotations);
585         added.removeAll(errorAnnotations);
586         unchanged=new ArrayList(errorAnnotations);
587         unchanged.retainAll(newAnnotations);
588         removed=errorAnnotations;
589         removed.removeAll(newAnnotations);
590         detachAnnotations(removed);
591         if (!added.isEmpty() && isDocumentLoaded()) {
592
593             // Partial fix of #33165 - document read-locking
594
final ArrayList finalAdded = added;
595             StyledDocument JavaDoc doc = getDocument();
596             Runnable JavaDoc docRenderer = new Runnable JavaDoc() {
597                 public void run() {
598                     LineCookie cookie = (LineCookie)getDataObject().getCookie(LineCookie.class);
599                     Line.Set lines = cookie.getLineSet();
600
601                     for (Iterator i=finalAdded.iterator();i.hasNext();) {
602                         ParserAnnotation ann=(ParserAnnotation)i.next();
603
604                         ann.attachToLineSet(lines);
605                     }
606                 }
607             };
608
609             if (doc != null) {
610                 doc.render(docRenderer);
611             } else {
612                 docRenderer.run();
613             }
614         }
615
616         errorAnnotations=unchanged;
617         errorAnnotations.addAll(added);
618     }
619
620
621     /** @return annotations for the given list of errors */
622     private Collection getAnnotations(List errors) {
623         HashMap map = new HashMap(2*errors.size());
624         int maxErrors = JavaSettings.getDefault().getParsingErrors();
625         for (Iterator it = errors.iterator(); it.hasNext();) {
626             ErrorInfo err = (ErrorInfo) it.next();
627             int line = err.getLineNumber();
628
629             if (line>0) { // annotate only errors with positive line number
630
int column = err.getColumn();
631                 String JavaDoc message = err.getDescription();
632                 ParserAnnotation anno = new ParserAnnotation(line, column, err.getSeverity(), message);
633
634                 // This is trying to ensure that annotations on the same
635
// line are "chained" (so we get a single annotation for
636
// multiple errors on a line).
637
// If we knew the errors were sorted by file & line number,
638
// this would be easy (and we wouldn't need to do the hashmap
639
// "sort"
640
Integer JavaDoc lineInt = new Integer JavaDoc(line);
641                 ParserAnnotation prev = (ParserAnnotation)map.get(lineInt);
642                 if (prev != null) {
643                     if (prev.getSeverity().equals(ErrorTypeEnum.WARNING)) {
644                         map.put(lineInt, anno);
645                         anno.chain(prev);
646                     } else {
647                         prev.chain(anno);
648                     }
649                 } else if (map.size() < maxErrors) {
650                     map.put(lineInt, anno);
651                 }
652             }
653         }
654         return map.values();
655     }
656
657     private static void detachAnnotations(Collection anns) {
658         Iterator i;
659
660         for (i=anns.iterator();i.hasNext();) {
661             Annotation ann=(Annotation)i.next();
662             if (ann.getAttachedAnnotatable() != null) {
663                 ann.detach();
664             }
665         }
666     }
667
668     private ClassPath getBootClassPath () {
669         ClassPath result;
670         if (this.bootClasspath == null || (result = (ClassPath)this.bootClasspath.get()) == null) {
671             result = ClassPath.getClassPath (getDataObject().getPrimaryFile(), ClassPath.BOOT);
672             if (result != null) {
673                 this.bootClasspath = new WeakReference JavaDoc (result);
674             }
675         }
676         return result;
677     }
678
679     private ClassPath getLibrariesPath () {
680         ClassPath result;
681         if (this.librariesClasspath == null || (result = (ClassPath)this.librariesClasspath.get()) == null) {
682             result = ClassPath.getClassPath (getDataObject().getPrimaryFile(), ClassPath.COMPILE);
683             if (result != null) {
684                 this.librariesClasspath = new WeakReference JavaDoc (result);
685             }
686         }
687         return result;
688     }
689
690     private ClassPath getSourcePath () {
691         ClassPath result;
692         if (this.sourceClasspath == null || (result = (ClassPath)this.sourceClasspath.get()) == null) {
693             result = ClassPath.getClassPath (getDataObject().getPrimaryFile(), ClassPath.SOURCE);
694             this.sourceClasspath = new WeakReference JavaDoc (result);
695         }
696         return result;
697     }
698
699     // ==================== SourceCookie.Editor methods =================
700

701     /** Returns a source element describing the hierarchy of the source.
702     * @return the element
703     * @deprecated Please use DataObject services to obtain java hierarchy.
704     */

705     public SourceElement getSource() {
706         return ((JavaDataObject)getDataObject()).getSource();
707     }
708
709     /** Translate a source element to text.
710     *
711     * @param element an element from the source hierarchy
712     * @return a text element
713     * @deprecated Please use DataObject's cookies to translate swing <-> org.openide.src
714     */

715     public javax.swing.text.Element JavaDoc sourceToText(org.openide.src.Element element) {
716         return null;
717     }
718
719     /** Translate a text element to a source element, if it is possible to do so.
720     *
721     * @param element a text element
722     * @return the element from the source hierarchy
723     * @exception NoSuchElementException if the text element doesn't match
724     * any element from the source hierarchy
725     * @deprecated Please use DataObject's cookies to translate swing <-> org.openide.src
726     */

727     public org.openide.src.Element textToSource(javax.swing.text.Element JavaDoc element) throws NoSuchElementException {
728         throw new NoSuchElementException();
729     }
730
731     /** Find the element at the specified offset in the document.
732     * @param offset The position of the element
733     * @return the element at the position.
734     * @deprecated Please use DataObject's cookies to translate swing <-> org.openide.src
735     */

736     public org.openide.src.Element findElement(int offset) {
737         return null;
738     }
739
740     // ==================== Guarded sections public methods =================
741

742     /**
743      * Creates an empty simple section at the given position.
744      * The position must not be within any existing guarded section or
745      * Java Element and the passed name must not be registered to other
746      * already existing section. The created section will initially contain
747      * one space and a newline.
748      * @return SimpleSection instance that can be used for generating text into
749      * the protected region
750      * @throws IllegalArgumentException if either the name has been already used, or
751      * the position is inside another section or Java Element.
752      * @throws BadLocationException if pos is outside of document's scope, or
753      * the document does not permit creating the guarded section.
754      */

755     public SimpleSection createSimpleSection(PositionRef pos, String JavaDoc name)
756         throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
757         checkOverlap(pos);
758         return doCreateSimpleSection(pos, name);
759     }
760
761     /*
762      * Creates a simple section over the given bounds in the source text.
763      * The bounds must not overlap with any existing section or Java Element
764      * and the passed name must not be registered to other
765      * already existing section. The section will then contain all the text
766      * inside the passed PositionBounds.
767      * @return SimpleSection instance that can be used for generating text into
768      * the protected region
769      * @throw IllegalArgumentException if either the name has been already used, or
770      * the bounds overlap with another section or Java Element.
771      * @throw BadLocationException if pos is outside of document's scope, or
772      * the document does not permit creating the guarded section.
773      */

774     public SimpleSection createSimpleSection(PositionBounds bounds, String JavaDoc name)
775         throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
776         checkOverlap(bounds);
777         return doCreateSimpleSection(bounds, name);
778     }
779
780     private void checkOverlap(PositionRef pos) {
781         Iterator it = sections.values().iterator();
782         while (it.hasNext()) {
783             GuardedSection s = (GuardedSection)it.next();
784             if (s.contains(pos, false)) {
785                 throw new IllegalArgumentException JavaDoc("Sections overlap"); // NOI18N
786
}
787         }
788     }
789
790     /*
791      * Tests if bounds overlap with any existing guarded section.
792      * @return <code>true</code> if bounds does not overlap with any section, otherwise <code>false</code>
793      */

794     public boolean testOverlap(PositionBounds bounds) {
795         try {
796             openDocument();
797         }
798         catch (IOException e) {
799             throw new IllegalArgumentException JavaDoc();
800         }
801         try {
802             checkOverlap(bounds,true);
803         } catch (IllegalArgumentException JavaDoc e){
804             return false;
805         }
806         return true;
807     }
808
809     private void checkOverlap(PositionBounds bounds) throws IllegalArgumentException JavaDoc {
810         checkOverlap(bounds,false);
811     }
812
813     private void checkOverlap(PositionBounds bounds,boolean allowHoles) throws IllegalArgumentException JavaDoc {
814         Collection c = new TreeSet(new GuardedPositionComparator());
815         c.addAll(sections.values());
816
817         Iterator it = c.iterator();
818         PositionRef begin = bounds.getBegin();
819         PositionRef end = bounds.getEnd();
820         int beginOffset = begin.getOffset();
821         int endOffset = end.getOffset();
822         GuardedSection starting = null;
823         boolean overlapOK = false;
824
825         while (it.hasNext()) {
826             GuardedSection s = (GuardedSection)it.next();
827             if (s.contains(begin, allowHoles) || s.contains(end, allowHoles))
828                 throw new IllegalArgumentException JavaDoc("Sections overlap"); // NOI18N
829
if (s.getBegin().getOffset() > beginOffset) {
830                 if (s.getBegin().getOffset() < endOffset) {
831                     throw new IllegalArgumentException JavaDoc("Sections overlap"); // NOI18N
832
}
833                 break;
834             }
835         }
836     }
837
838     private SimpleSection doCreateSimpleSection(final PositionBounds bounds, final String JavaDoc name)
839     throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
840         StyledDocument JavaDoc loadedDoc = null;
841         try {
842             loadedDoc = openDocument();
843         }
844         catch (IOException e) {
845             throw new IllegalArgumentException JavaDoc("Cannot load document"); // NOI18N
846
}
847         final StyledDocument JavaDoc doc = loadedDoc;
848         final SimpleSection[] sect = new SimpleSection[] { null };
849
850         Util.ExceptionRunnable run = new Util.ExceptionRunnable() {
851             public void run() throws Exception JavaDoc {
852                 sect[0] = new SimpleSection(name,
853                 createBounds(bounds.getBegin().getOffset(), bounds.getEnd().getOffset(), false)
854                 );
855                 sections.put(sect[0].getName(), sect[0]);
856                 sect[0].markGuarded(doc);
857             }
858         };
859         try {
860             Util.runAtomic(doc, run);
861         notifyModified();
862         }
863         catch (Exception JavaDoc e) {
864             if (e instanceof BadLocationException JavaDoc)
865                 throw (BadLocationException JavaDoc) e;
866             else
867                 throw new IllegalArgumentException JavaDoc();
868         }
869         return sect[0];
870     }
871
872     private SimpleSection doCreateSimpleSection(final PositionRef pos, final String JavaDoc name)
873         throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
874         StyledDocument JavaDoc loadedDoc = null;
875         try {
876             loadedDoc = openDocument();
877         }
878         catch (IOException e) {
879             throw new IllegalArgumentException JavaDoc();
880         }
881         final StyledDocument JavaDoc doc = loadedDoc;
882         final SimpleSection[] sect = new SimpleSection[] { null };
883
884         Util.ExceptionRunnable run = new Util.ExceptionRunnable() {
885                                          public void run() throws Exception JavaDoc {
886                                              int where = pos.getOffset();
887                                              doc.insertString(where, "\n \n", null); // NOI18N
888
sect[0] = new SimpleSection(name,
889                                                                          createBounds(where + 1, where + 3, false)
890                                                                         );
891                                              sections.put(sect[0].getName(), sect[0]);
892                                              sect[0].markGuarded(doc);
893                                          }
894                                      };
895         try {
896             Util.runAtomic(doc, run);
897         notifyModified();
898         }
899         catch (Exception JavaDoc e) {
900             if (e instanceof BadLocationException JavaDoc)
901                 throw (BadLocationException JavaDoc) e;
902             else
903                 throw new IllegalArgumentException JavaDoc();
904         }
905         return sect[0];
906     }
907
908     /**
909      * Create new simple guarded section at a specified place.
910      * @param previous section to create the new one after
911      * @param name the name of the new section
912      * @exception IllegalArgumentException if the name is already in use
913      * @exception BadLocationException if it is not possible to create a
914      * new guarded section here
915      */

916     public SimpleSection createSimpleSectionAfter(final GuardedSection previous,
917     final String JavaDoc name)
918     throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
919         PositionBounds bounds;
920         if (previous instanceof SimpleSection)
921             bounds = ((SimpleSection) previous).bounds;
922         else
923             bounds = ((InteriorSection) previous).bottom;
924         if ((previous == null) || (!previous.valid))
925             throw new IllegalArgumentException JavaDoc("Invalid guarded block"); // NOI18N
926

927         return doCreateSimpleSection(bounds.getEnd(), name);
928     }
929
930     public InteriorSection createInteriorSection(PositionRef pos, String JavaDoc name)
931     throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
932         checkOverlap(pos);
933         return doCreateInteriorSection(pos, name);
934     }
935
936     public InteriorSection createInteriorSectionAfter(GuardedSection previous,
937         String JavaDoc name) throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
938             PositionBounds bounds;
939         if (previous instanceof SimpleSection)
940             bounds = ((SimpleSection) previous).bounds;
941         else
942             bounds = ((InteriorSection) previous).bottom;
943         if ((previous == null) || (!previous.valid))
944             throw new IllegalArgumentException JavaDoc("Invalid guarded block"); // NOI18N
945
return doCreateInteriorSection(bounds.getEnd(), name);
946     }
947
948     public InteriorSection createInteriorSection(PositionBounds bounds, PositionBounds interior,
949         String JavaDoc name) throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
950         checkOverlap(bounds);
951         if (bounds.getBegin().getOffset() > interior.getEnd().getOffset() ||
952             bounds.getEnd().getOffset() < interior.getEnd().getOffset())
953             throw new IllegalArgumentException JavaDoc("Interior is not nested."); // NOI18N
954
return doCreateInteriorSection(bounds, interior, name);
955     }
956
957     private InteriorSection doCreateInteriorSection(final PositionBounds bounds,
958     final PositionBounds interiorBounds, final String JavaDoc name)
959     throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
960         StyledDocument JavaDoc loadedDoc = null;
961         try {
962             loadedDoc = openDocument();
963         }
964         catch (IOException e) {
965             throw new IllegalArgumentException JavaDoc("Cannot load document"); // NOI18N
966
}
967
968         final StyledDocument JavaDoc doc = loadedDoc;
969         final InteriorSection[] sect = new InteriorSection[] { null };
970
971         Util.ExceptionRunnable run = new Util.ExceptionRunnable() {
972             public void run() {
973                 sect[0] = new InteriorSection(name,
974                 createBounds(bounds.getBegin().getOffset(), interiorBounds.getBegin().getOffset(), false),
975                 createBounds(interiorBounds.getBegin().getOffset(), interiorBounds.getEnd().getOffset(), true),
976                 createBounds(interiorBounds.getEnd().getOffset(), bounds.getEnd().getOffset(), false)
977                 );
978                 sections.put(sect[0].getName(), sect[0]);
979                 sect[0].markGuarded(doc);
980             }
981         };
982         try {
983             Util.runAtomic(doc, run);
984         notifyModified();
985         }
986         catch (Exception JavaDoc e) {
987             if (e instanceof BadLocationException JavaDoc)
988                 throw (BadLocationException JavaDoc) e;
989             else
990                 throw new IllegalArgumentException JavaDoc();
991         }
992         return sect[0];
993     }
994
995     /** Create new interior guarded section at a specified place.
996     * @param pos section to create the new one after
997     * @param name the name of the new section
998     * @exception IllegalArgumentException if the name is already in use
999     * @exception BadLocationException if it is not possible to create a
1000    * new guarded section here
1001    */

1002    private InteriorSection doCreateInteriorSection(final PositionRef pos,
1003            final String JavaDoc name)
1004    throws IllegalArgumentException JavaDoc, BadLocationException JavaDoc {
1005        StyledDocument JavaDoc loadedDoc = null;
1006        try {
1007            loadedDoc = openDocument();
1008        }
1009        catch (IOException e) {
1010            throw new IllegalArgumentException JavaDoc();
1011        }
1012
1013        final StyledDocument JavaDoc doc = loadedDoc;
1014        final InteriorSection[] sect = new InteriorSection[] { null };
1015
1016        Util.ExceptionRunnable run = new Util.ExceptionRunnable() {
1017            public void run() throws Exception JavaDoc {
1018                int where = pos.getOffset();
1019                doc.insertString(where, "\n \n \n \n", null); // NOI18N
1020
sect[0] = new InteriorSection(name,
1021                createBounds(where + 1, where + 3, false),
1022                createBounds(where + 3, where + 5, true),
1023                createBounds(where + 5, where + 7, false)
1024                );
1025                sections.put(sect[0].getName(), sect[0]);
1026                sect[0].markGuarded(doc);
1027            }
1028        };
1029        try {
1030            Util.runAtomic(doc, run);
1031        notifyModified();
1032        }
1033        catch (Exception JavaDoc e) {
1034            if (e instanceof BadLocationException JavaDoc)
1035                throw (BadLocationException JavaDoc) e;
1036            else
1037                throw new IllegalArgumentException JavaDoc();
1038        }
1039        return sect[0];
1040    }
1041
1042    /** Try to find the simple section of the given name.
1043    * @param name the name of the requested section
1044    * @return the found guarded section or <code>null</code> if there is no section
1045    * of the given name
1046    */

1047    public SimpleSection findSimpleSection(String JavaDoc name) {
1048        GuardedSection s = findSection(name);
1049        return (s instanceof SimpleSection) ? (SimpleSection) s : null;
1050    }
1051
1052    /** Try to find the interior section of the given name.
1053    * @param name the name of the looked-for section
1054    * @return the found guarded section or <code>null</code> if there is no section
1055    * of the given name
1056    */

1057    public InteriorSection findInteriorSection(String JavaDoc name) {
1058        GuardedSection s = findSection(name);
1059        return (s instanceof InteriorSection) ? (InteriorSection) s : null;
1060    }
1061
1062    /** Try to find the section of the given name.
1063    * @param name the name of the looked-for section
1064    * @return the found guarded section or <code>null</code> if there is no section
1065    * of the given name
1066    */

1067    public GuardedSection findSection(String JavaDoc name) {
1068        try {
1069            StyledDocument JavaDoc doc = openDocument ();
1070            synchronized (this) {
1071                if (sections != null)
1072                    return (GuardedSection) sections.get(name);
1073            }
1074        }
1075        catch (IOException e) {
1076        }
1077        return null;
1078    }
1079
1080    /** Get all sections.
1081    * @return an iterator of {@link JavaEditor.GuardedSection}s
1082    */

1083    public Iterator getGuardedSections() {
1084        try {
1085            StyledDocument JavaDoc doc = openDocument ();
1086            synchronized (this) {
1087                if (sections != null)
1088                    return ((HashMap)sections.clone()).values().iterator();
1089            }
1090        }
1091        catch (IOException e) {
1092        }
1093        return Collections.EMPTY_SET.iterator();
1094    }
1095
1096    /** Get all section names.
1097    * @return an iterator of {@link String}s
1098    */

1099    public Iterator getGuardedSectionNames() {
1100        try {
1101            StyledDocument JavaDoc doc = openDocument ();
1102            synchronized (this) {
1103                if (sections != null)
1104                    return ((HashMap)sections.clone()).keySet().iterator();
1105            }
1106        }
1107        catch (IOException e) {
1108            e.printStackTrace();
1109        }
1110        return Collections.EMPTY_SET.iterator();
1111    }
1112
1113    /**
1114     * Finds a position not obscured by a guarded block that is inside the given
1115     * bounds. Favors positions at the beginning of the passed bounds. Returns null,
1116     * if the document cannot be loaded.
1117     * @param bnds bounds to search within
1118     * @return PositionRef that can be safely written into.
1119     */

1120    public PositionRef findFreePosition(PositionBounds bnds) {
1121        StyledDocument JavaDoc doc;
1122        try {
1123            doc = openDocument();
1124        } catch (IOException ex) {
1125            return null;
1126        }
1127
1128        PositionRef beginPos = bnds.getBegin();
1129        int startOffs = beginPos.getOffset();
1130
1131        TreeSet set = new TreeSet(SECTION_COMPARATOR);
1132        set.addAll(this.sections.values());
1133        for (Iterator it = set.iterator(); it.hasNext(); ) {
1134            GuardedSection s = (GuardedSection)it.next();
1135            PositionRef start = s.getBegin();
1136            if (start.getOffset() > startOffs) {
1137                // no section at the start.
1138
break;
1139            }
1140            if (s.contains(beginPos, false)) {
1141                // got guarded block that contains
1142
PositionRef after = s.getPositionAfter();
1143                if (after.getOffset() > bnds.getEnd().getOffset()) {
1144                    return null;
1145                }
1146                return after;
1147            }
1148        }
1149        return beginPos;
1150    }
1151
1152    protected CloneableEditor createCloneableEditor() {
1153        return new JavaEditorComponent(this);
1154    }
1155
1156// // ==================== Misc not-public methods ========================
1157
//
1158
// /** Method for creation of the java editor component
1159
// * - accessible from the innerclass.
1160
// */
1161
// JavaEditorComponent createJavaEditorComponent() {
1162
// DataObject obj = getDataObject ();
1163
// JavaEditorComponent editor = new JavaEditorComponent(this);
1164
// //editor.setIcon(obj.getNodeDelegate().getIcon(java.beans.BeanInfo.ICON_COLOR_16x16));
1165
//
1166
// // dock into editor mode if possible
1167
// Mode editorMode = WindowManager.getDefault().findMode(EDITOR_MODE);
1168
// //if (editorMode != null)
1169
// // editorMode.dockInto(editor);
1170
//
1171
// return editor;
1172
// }
1173

1174    /** Set all sections as invalid. It is called from closeLast method
1175    * of the JavaEditorComponent.
1176    */

1177    synchronized void clearSections() {
1178        if (sections != null) {
1179            Iterator it = ((HashMap)sections.clone()).values().iterator();
1180            while (it.hasNext()) {
1181                GuardedSection sect = (GuardedSection) it.next();
1182                sect.valid = false;
1183            }
1184            sections = null;
1185        }
1186    }
1187
1188    PositionRef findUnguarded(PositionRef fromWhere, boolean allowHoles, boolean after) {
1189    Iterator it = getGuardedSections();
1190
1191    while (it.hasNext()) {
1192        GuardedSection sect = (GuardedSection)it.next();
1193        if (sect.contains(fromWhere, allowHoles)) {
1194        if (after) {
1195            return sect.getPositionAfter();
1196        } else {
1197            return sect.getPositionBefore();
1198        }
1199        }
1200    }
1201    return fromWhere;
1202    }
1203    
1204    private static class JavaEditorEnv extends DataEditorSupport.Env {
1205
1206        static final long serialVersionUID = -6792511207355520950L;
1207        
1208        public JavaEditorEnv(DataObject obj) {
1209            super(obj);
1210        }
1211        
1212        protected FileObject getFile() {
1213            return getDataObject().getPrimaryFile();
1214        }
1215        protected FileLock takeLock() throws IOException {
1216            return ((JavaDataObject) getDataObject()).getPrimaryEntry().takeLock();
1217        }
1218        public CloneableOpenSupport findCloneableOpenSupport() {
1219            // must be sync with cookies.add(EditorCookie.class, factory);
1220
// #12938 XML files do not persist in Source editor
1221
return (CloneableOpenSupport) getDataObject().getCookie(EditorCookie.class);
1222        }
1223    }
1224    
1225    /** The real component of the Java editor.
1226    * Subclasses should not attempt to work with this;
1227    * if they require special editing support, separate windows
1228    * should be created by overriding (e.g.) {@link EditorSupport#open}.
1229    */

1230    public static class JavaEditorComponent extends CloneableEditor {
1231        /** Default delay between cursor movement and updating selected element nodes. */
1232        static final int SELECTED_NODES_DELAY = 1000;
1233
1234        /** Timer which countdowns the "update selected element node" time. */ // NOI18N
1235
Timer JavaDoc timerSelNodes;
1236
1237        /** The support, subclass of EditorSupport */
1238        JavaEditor support;
1239
1240        /** Listener on caret movements */
1241        CaretListener caretListener;
1242
1243        /**
1244         * Toolbar that is displayed at the top of the editor window.
1245         * Lazily initialized in enableToolBar / createToolBar.
1246         */

1247        Component JavaDoc toolBar;
1248
1249        /** The last caret offset position. */
1250        int lastCaretOffset = -1;
1251
1252        static final long serialVersionUID =6223349196427270209L;
1253
1254        /** Only for externalization */
1255        public JavaEditorComponent () {
1256            super();
1257        }
1258
1259        /** Creates new editor */
1260        public JavaEditorComponent (CloneableEditorSupport sup) {
1261            super(sup);
1262            initialize();
1263        }
1264
1265        private transient RequestProcessor.Task selectionTask = null;
1266
1267        private final RequestPoster elementSelectionPoster = new RequestPoster();
1268    
1269        /** Selects element at the given position. */
1270        void selectElementsAtOffset(final int offset) {
1271            elementSelectionPoster.post(new Runnable JavaDoc() {
1272                public void run() {
1273                    ((JMManager) JMManager.getManager()).waitScanFinished();
1274                    final DataObject d = support.getDataObject();
1275                    if (!isActiveTC() || d == null || !d.isValid() || d.isTemplate()) {
1276                      return;
1277                    }
1278                    
1279                    final Node n;
1280                    n = (Node) Children.MUTEX.readAccess(new Mutex.Action() {
1281                        public Object JavaDoc run() {
1282                            return createNode(offset, d);
1283                        }
1284                    });
1285                    SwingUtilities.invokeLater(new Runnable JavaDoc() {
1286                        public void run() {
1287                            setActivatedNodes((n != null) ? new Node[] { n } : new Node[] {} );
1288                        }
1289                    });
1290                }
1291            });
1292        }
1293        
1294        private Node createNode(int offset, DataObject d) {
1295            SourceNodeFactory factory = SourceNodes.getExplorerFactory();
1296            Node n = null;
1297            Element element;
1298            Element currentElement = null;
1299            Node[] nodes = getActivatedNodes();
1300            if (nodes!=null && nodes.length == 1) {
1301                currentElement = (Element) nodes[0].getLookup().lookup(Element.class);
1302            }
1303            try {
1304                JMManager.getTransactionMutex().addPriorityThread();
1305                JavaMetamodel.getDefaultRepository().beginTrans(false);
1306                try {
1307                    FileObject fo=d.getPrimaryFile();
1308                    JavaModel.setClassPath(fo);
1309                    Resource res = JavaMetamodel.getManager().getResource(fo);
1310                    element = res == null? null: findElement(res, offset);
1311                    if (element != null && currentElement != null && element.isValid() && currentElement.isValid()) {
1312                        if (element.equals(currentElement)) {
1313                            return nodes[0];
1314                        }
1315                    }
1316                    
1317                    if (element instanceof Field) {
1318                        n = factory.createFieldNode((Field) element);
1319                    } else if (element instanceof Attribute) {
1320                        n = factory.createAnnotationTypeMethodNode((Attribute) element);
1321                    } else if (element instanceof AnnotationType) {
1322                        n = factory.createAnnotationTypeNode((AnnotationType) element);
1323                    } else if (element instanceof Constructor) {
1324                        n = factory.createConstructorNode((Constructor) element);
1325                    } else if (element instanceof EnumConstant) {
1326                        n = factory.createEnumConstantNode((EnumConstant) element);
1327                    } else if (element instanceof Initializer) {
1328                        n = factory.createInitializerNode((Initializer) element);
1329                    } else if (element instanceof Method) {
1330                        n = factory.createMethodNode((Method) element);
1331                    } else if (element instanceof JavaEnum) {
1332                        n = factory.createEnumNode((JavaEnum) element);
1333                    } else if (element instanceof JavaClass) {
1334                        n = factory.createClassNode((JavaClass) element);
1335                    } else if (element instanceof Resource) {
1336                        n = d.getNodeDelegate();
1337                    } else {
1338                        n = null;
1339                    }
1340                } finally {
1341                    JavaMetamodel.getDefaultRepository().endTrans();
1342                }
1343            } catch (JmiException e) {
1344                ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
1345            }
1346            
1347            return n;
1348        }
1349        
1350        /**
1351         * finds class member on particular offset. It works around the issue
1352         * of body parsing
1353         * @param r resource to look up
1354         * @param offset offset
1355         * @return the class member
1356         */

1357        private static Element findElement(Resource r, int offset) {
1358            Iterator cit = r.getClassifiers().iterator();
1359            Element el = r;
1360            while (cit.hasNext()) {
1361                JavaClass jc = (JavaClass) cit.next();
1362                PositionBounds bounds = JavaMetamodel.getManager().getElementPosition(jc);
1363                if (bounds != null && offset >= bounds.getBegin().getOffset() &&
1364                        offset <= bounds.getEnd().getOffset()) {
1365                    el = findElement(jc, offset);
1366                    break;
1367                }
1368            }
1369            return el;
1370        }
1371        
1372        /**
1373         * @see #findElement(org.netbeans.jmi.javamodel.Resource, int)
1374         */

1375        private static Element findElement(JavaClass jc, int offset) {
1376            Iterator classIt = jc.getFeatures().iterator();
1377            Element el = jc;
1378            while (classIt.hasNext()) {
1379                ClassMember cm = (ClassMember) classIt.next();
1380                PositionBounds bounds = JavaMetamodel.getManager().getElementPosition(cm);
1381                if (offset >= bounds.getBegin().getOffset() &&
1382                        offset <= bounds.getEnd().getOffset()) {
1383                    if (cm instanceof JavaClass) {
1384                        el = findElement((JavaClass) cm, offset);
1385                    } else {
1386                        el = cm;
1387                    }
1388                    break;
1389                }
1390            }
1391            
1392            return el;
1393        }
1394
1395        protected boolean isActiveTC() {
1396            return getRegistry().getActivated() == JavaEditorComponent.this;
1397        }
1398
1399        protected void notifyParsingDone() {
1400// if (lastCaretOffset != -1) {
1401
// selectElementsAtOffset(lastCaretOffset);
1402
// }
1403
}
1404
1405        /**
1406         * Refreshes the activated node immediately. Provides system actions
1407         * based on the node activated at the time of popu invoke.
1408         */

1409        public SystemAction[] getSystemActions() {
1410            selectElementsAtOffset(lastCaretOffset);
1411            timerSelNodes.stop();
1412            return super.getSystemActions();
1413        }
1414
1415        /** Obtain a support for this component */
1416        private void initialize () {
1417            support = (JavaEditor) cloneableEditorSupport();
1418            if (support==null) //support can be null only if the component is not valid
1419
return; //and in this case "this" should be discarded (initialization is not needed)
1420

1421            timerSelNodes = new Timer JavaDoc(200, new ActionListener JavaDoc() {
1422                                          public void actionPerformed(ActionEvent JavaDoc e) {
1423                                              Timer JavaDoc t=support.timer;
1424                                              
1425                                              if (t!=null && t.isRunning()) {
1426                                                  timerSelNodes.restart();
1427                                                  return;
1428                                              }
1429                                              
1430                                              if (!isActiveTC()) {
1431                                                  // component is not active; do nothing to not initialize toolbar or code folding
1432
// especialy do not call getEditorPane(); see issue #49956
1433
return;
1434                                              }
1435                                              if (lastCaretOffset == -1 && getEditorPane() != null) {
1436                                                  Caret JavaDoc caret = getEditorPane().getCaret();
1437                                                  if (caret != null)
1438                                                    lastCaretOffset = caret.getDot();
1439                                              }
1440                                              selectElementsAtOffset(lastCaretOffset);
1441                                          }
1442                                      });
1443            timerSelNodes.setInitialDelay(200);
1444            timerSelNodes.setRepeats(false);
1445            caretListener = new CaretListener() {
1446                                public void caretUpdate(CaretEvent e) {
1447                                    support.restartTimer(true);
1448                                    restartTimerSelNodes(e.getDot());
1449                                }
1450                            };
1451            timerSelNodes.restart();
1452        }
1453
1454        /** Restart the timer which updates the selected nodes after the specified delay from
1455        * last caret movement.
1456        */

1457        void restartTimerSelNodes(int pos) {
1458            timerSelNodes.setInitialDelay(SELECTED_NODES_DELAY);
1459            timerSelNodes.restart();
1460            lastCaretOffset = pos;
1461        }
1462
1463        /* This method is called when parent window of this component has focus,
1464        * and this component is preferred one in it.
1465        */

1466        protected void componentActivated () {
1467            JEditorPane p = getEditorPane();
1468            if (p != null)
1469                p.addCaretListener(caretListener);
1470            super.componentActivated ();
1471            // give focus to the editor pane, rather than the toolbar:
1472
if (p != null)
1473                p.requestFocusInWindow();
1474            if ((support.timer==null || !support.timer.isRunning())) {
1475                support.restartTimer(false, true);
1476            }
1477            if (!support.hasAnnotations) { // if we don't have annotations, schedule parsing
1478
ERROR_ANNOTATION_QUEUE.post(new Runnable JavaDoc() {
1479                    public void run() {
1480                        if (support.overriddensSupport != null) { //#43491
1481
support.overriddensSupport.processOverriddenAnnotation();
1482                        }
1483                    }
1484                }, 1000, Thread.MIN_PRIORITY);
1485            }
1486            support.attachParsingListener();
1487        }
1488
1489        public void requestFocus() {
1490            super.requestFocus();
1491            JEditorPane p = getEditorPane();
1492            if (p != null) {
1493                p.requestFocus();
1494            }
1495        }
1496        
1497        public boolean requestFocusInWindow() {
1498            super.requestFocusInWindow();
1499            JEditorPane p = getEditorPane();
1500            if (p != null) {
1501                return p.requestFocusInWindow();
1502            } else {
1503                return false;
1504            }
1505        }
1506
1507        /*
1508        * This method is called when parent window of this component losts focus,
1509        * or when this component losts preferrence in the parent window.
1510        */

1511        protected void componentDeactivated () {
1512            JEditorPane p = getEditorPane();
1513            if (p != null)
1514                p.removeCaretListener(caretListener);
1515            support.removeParsingListener();
1516            synchronized (this) {
1517                if (selectionTask != null) {
1518                    selectionTask.cancel();
1519                    selectionTask = null;
1520                }
1521            }
1522            support.stopTimerIfPossible();
1523            super.componentDeactivated ();
1524        }
1525
1526        /** Deserialize this top component.
1527        * @param in the stream to deserialize from
1528        */

1529        public void readExternal (ObjectInput in)
1530        throws IOException, ClassNotFoundException JavaDoc {
1531            super.readExternal(in);
1532            initialize();
1533        }
1534
1535    } // end of JavaEditorComponent inner class
1536

1537    /** Takes the section descriptors from the GuardedReader and
1538    * fills the table 'sections', also marks as guarded all sections
1539    * in the given document.
1540    * @param is Where to take the guarded section descriptions.
1541    * @param doc Where to mark guarded.
1542    */

1543    private void fillSections(GuardedReader is, StyledDocument JavaDoc doc) {
1544        JavaEditor.SectionDesc descBegin = null;
1545
1546        Iterator it = is.list.iterator();
1547        while (it.hasNext()) {
1548            SectionDesc descCurrent = (SectionDesc) it.next();
1549            GuardedSection sect = null;
1550            switch (descCurrent.type) {
1551            case T_LINE:
1552                sect = new SimpleSection(descCurrent.name,
1553                                         createBounds(descCurrent.begin,
1554                                                      descCurrent.end, false));
1555                break;
1556
1557            case T_BEGIN:
1558            case T_HEADER:
1559            case T_FIRST:
1560                descBegin = descCurrent;
1561                break;
1562
1563            case T_HEADEREND:
1564                if ((descBegin != null) &&
1565                        ((descBegin.type == T_HEADER) || (descBegin.type == T_FIRST)) &&
1566                        (descCurrent.name.equals(descBegin.name))
1567                   ) {
1568                    descBegin.end = descCurrent.end;
1569                }
1570                else {
1571                    //SYNTAX ERROR - ignore it.
1572
descBegin = null;
1573                }
1574                break;
1575
1576            case T_END:
1577            case T_LAST:
1578                if ((descBegin != null) && (descBegin.name.equals(descCurrent.name))) {
1579                    if ((descBegin.type == T_BEGIN) && (descCurrent.type == T_END)) {
1580                        // simple section
1581
sect = new SimpleSection(descCurrent.name,
1582                                                 createBounds(descBegin.begin,
1583                                                              descCurrent.end, false));
1584                        break;
1585                    }
1586                    if (((descBegin.type == T_FIRST) && (descCurrent.type == T_LAST)) ||
1587                            ((descBegin.type == T_HEADER) && (descCurrent.type == T_END))) {
1588                        // interior section
1589
sect = new InteriorSection(descCurrent.name,
1590                                                   createBounds(descBegin.begin, descBegin.end, false),
1591                                                   createBounds(descBegin.end, descCurrent.begin, true),
1592                                                   createBounds(descCurrent.begin, descCurrent.end, false)
1593                                                  );
1594                        break;
1595                    }
1596                }
1597                //SYNTAX ERROR - ignore it.
1598
descBegin = null;
1599                break;
1600            }
1601
1602            if (sect != null) {
1603                sections.put(sect.getName(), sect);
1604                descBegin = null;
1605                sect.markGuarded(doc);
1606            }
1607        }
1608    }
1609
1610    /** Simple creates the bounds for the two offsets. */
1611    public PositionBounds createBounds(int begin, int end, boolean dir) {
1612        if (!dir) {
1613            return new PositionBounds(
1614                       createPositionRef(begin, Position.Bias.Forward),
1615                       createPositionRef(end, Position.Bias.Backward)
1616                   );
1617        }
1618        else {
1619            return new PositionBounds(
1620                       createPositionRef(begin, Position.Bias.Backward),
1621                       createPositionRef(end, Position.Bias.Forward)
1622                   );
1623        }
1624    }
1625
1626    public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1627        UndoManager undo = JavaMetamodel.getUndoManager();
1628        if (undo.isUndoAvailable() || undo.isRedoAvailable()) {
1629            getUndoRedo().discardAllEdits();
1630        }
1631    }
1632
1633    // ==================== Public inner classes ========================
1634

1635    /** Represents one guarded section.
1636    */

1637    public abstract class GuardedSection extends Object JavaDoc {
1638        /** Name of the section. */
1639        String JavaDoc name;
1640
1641        /** If the section is valid or if it was removed. */
1642        boolean valid;
1643
1644        /** Get the name of the section.
1645        * @return the name
1646        */

1647        public String JavaDoc getName() {
1648            return name;
1649        }
1650
1651        /** Creates new section.
1652        * @param name Name of the new section.
1653        */

1654        GuardedSection(String JavaDoc name) {
1655            this.name = name;
1656            valid = true;
1657        }
1658
1659        /** Set the name of the section.
1660        * @param name the new name
1661        * @exception PropertyVetoException if the new name is already in use
1662        */

1663        public void setName(String JavaDoc name) throws PropertyVetoException JavaDoc {
1664            if (!this.name.equals(name)) {
1665                synchronized (JavaEditor.this) {
1666                    if (valid) {
1667                        if (sections.get(name) != null)
1668                            throw new PropertyVetoException JavaDoc("", new PropertyChangeEvent JavaDoc(this, "name", this.name, name)); // NOI18N
1669
sections.remove(this.name);
1670                        this.name = name;
1671                        sections.put(name, this);
1672                    }
1673                }
1674            }
1675        }
1676
1677        /** Deletes the text of the section and
1678        * removes it from the table. The section will then be invalid
1679        * and it will be impossible to use its methods.
1680        *
1681        * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
1682        */

1683        public boolean deleteSection() {
1684            synchronized (JavaEditor.this) {
1685                if (valid) {
1686                    try {
1687                        sections.remove(name);
1688                        // get document should always return the document, when section
1689
// is deleted, because it is still valid (and valid is only
1690
// when document is loaded.
1691
unmarkGuarded(getDocument());
1692                        deleteText();
1693                        valid = false;
1694                        return true;
1695                    }
1696                    catch (BadLocationException JavaDoc e) {
1697                    }
1698                    catch (IOException e) {
1699                    }
1700                }
1701                return false;
1702            }
1703        }
1704
1705        /**
1706         * Tests if the section is still valid - it is not removed from the
1707         * source.
1708         */

1709        public boolean isValid() {
1710            return valid;
1711        }
1712
1713        /**
1714         * Removes the section from the Document, but retains the text contained
1715         * within. The method should be used to unprotect a region of code
1716         * instead of calling NbDocument.
1717         * @return true if the operation succeeded.
1718         */

1719        public boolean removeSection() {
1720            synchronized (JavaEditor.this) {
1721                if (!valid)
1722                    return false;
1723                sections.remove(name);
1724                // get document should always return the document, when section
1725
// is deleted, because it is still valid (and valid is only
1726
// when document is loaded.
1727
unmarkGuarded(getDocument());
1728                valid = false;
1729                return true;
1730            }
1731        }
1732
1733        /** Delete one new-line character before the specified offset.
1734        * This method is used when guarded blocks are deleted. When new guarded block is created,
1735        * there is added one more new-line before it, so this method remove this char in the end of
1736        * guarded block life cycle.
1737        * It works only when there is "\n" char before the offset and no problem occured (IOException...)
1738        * @param offset The begin of removed guarded block.
1739        */

1740        void deleteNewLineBeforeBlock(int offset) {
1741            if (offset > 1) {
1742                try {
1743                    PositionBounds b = createBounds(offset - 1, offset, true);
1744                    String JavaDoc s = b.getText();
1745                    if (s.equals("\n")) { // NOI18N
1746
b.setText(""); // NOI18N
1747
}
1748                }
1749                catch (IOException e) {
1750                }
1751                catch (BadLocationException JavaDoc e) {
1752                }
1753            }
1754        }
1755
1756        /** Opens the editor and set cursor to this guarded section.
1757        */

1758        public void openAt() {
1759            JavaEditor.this.openAt(getBegin());
1760        }
1761
1762        /** Set the text contained in this section.
1763        * Newlines are automatically added to all text segments handled,
1764        * unless there was already one.
1765        * All guarded blocks must consist of entire lines.
1766        * This applies to the contents of specific guard types as well.
1767        * @param bounds the bounds indicating where the text should be set
1768        * @param text the new text
1769        * @param minLen If true the text has to have length more than 2 chars.
1770        * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
1771        */

1772        protected boolean setText(PositionBounds bounds, String JavaDoc text, boolean minLen) {
1773            if (!valid)
1774                return false;
1775
1776            // modify the text - has to end with new line and the length
1777
// has to be more then 2 characters
1778
if (minLen) {
1779                if (text.length() == 0)
1780                    text = " \n"; // NOI18N
1781
else if (text.length() == 1)
1782                    text = text.equals("\n") ? " \n" : text + "\n"; // NOI18N
1783
}
1784
1785            if (!text.endsWith("\n")) // NOI18N
1786
text = text + "\n"; // NOI18N
1787

1788            try {
1789                bounds.setText(text);
1790                return true;
1791            }
1792            catch (BadLocationException JavaDoc e) {
1793            }
1794            catch (IOException e) {
1795            }
1796            return false;
1797        }
1798
1799        /** Marks or unmarks the section as guarded.
1800        * @param doc The styled document where this section placed in.
1801        * @param bounds The rangeof text which should be marked or unmarked.
1802        * @param mark true means mark, false unmark.
1803        */

1804        void markGuarded(StyledDocument JavaDoc doc, PositionBounds bounds, boolean mark) {
1805            int begin = bounds.getBegin().getOffset();
1806            int end = bounds.getEnd().getOffset();
1807            if (mark)
1808                NbDocument.markGuarded(doc, begin, end - begin);
1809            else
1810                NbDocument.unmarkGuarded(doc, begin, end - begin);
1811        }
1812
1813        /** Marks the section as guarded.
1814        * @param doc The styled document where this section placed in.
1815        */

1816        abstract void markGuarded(StyledDocument JavaDoc doc);
1817
1818        /** Unmarks the section as guarded.
1819        * @param doc The styled document where this section placed in.
1820        */

1821        abstract void unmarkGuarded(StyledDocument JavaDoc doc);
1822
1823        /** Deletes the text in the section.
1824        * @exception BadLocationException
1825        * @exception IOException
1826        */

1827        abstract void deleteText() throws BadLocationException JavaDoc, IOException;
1828
1829        /** Gets the begin of section. To this position is set the cursor
1830        * when section is open in the editor.
1831        */

1832        public abstract PositionRef getBegin();
1833
1834        /** Gets the text contained in the section.
1835        * @return The text contained in the section.
1836        */

1837        public abstract String JavaDoc getText();
1838
1839        /** Assures that a position is not inside the guarded section. Complex guarded sections
1840         * that contain portions of editable text can return true if the tested position is
1841         * inside one of such portions provided that permitHoles is true.
1842         * @param pos position in question
1843         * @param permitHoles if false, guarded section is taken as a monolithic block
1844         * without any holes in it regadless of its complexity.
1845 */

1846        public abstract boolean contains(PositionRef pos,boolean permitHoles);
1847        /** Returns a position after the whole guarded block that is safe for insertions.
1848 */

1849        public abstract PositionRef getPositionAfter();
1850        /** Returns position before the whole guarded block that is safe for insertions.
1851 */

1852        public abstract PositionRef getPositionBefore();
1853    }
1854
1855    /** Represents a simple guarded section.
1856    * It consists of one contiguous block.
1857    */

1858    public final class SimpleSection extends GuardedSection {
1859        /** Text range of the guarded section. */
1860        PositionBounds bounds;
1861
1862        /** Creates new section.
1863        * @param name Name of the new section.
1864        * @param bounds The range of the section.
1865        */

1866        SimpleSection(String JavaDoc name, PositionBounds bounds) {
1867            super(name);
1868            this.bounds = bounds;
1869        }
1870
1871        /** Set the text of the section.
1872        * @param text the new text
1873        * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
1874        * @see JavaEditor.GuardedSection#setText
1875        */

1876        public boolean setText(String JavaDoc text) {
1877            return setText(bounds, text, true);
1878        }
1879
1880        /** Deletes the text in the section.
1881        * @exception BadLocationException
1882        * @exception IOException
1883        */

1884        void deleteText() throws BadLocationException JavaDoc, IOException {
1885            bounds.setText(""); // NOI18N
1886
deleteNewLineBeforeBlock(bounds.getBegin().getOffset());
1887        }
1888
1889        /** Marks the section as guarded.
1890        * @param doc The styled document where this section placed in.
1891        */

1892        void markGuarded(StyledDocument JavaDoc doc) {
1893            markGuarded(doc, bounds, true);
1894        }
1895
1896        /** Unmarks the section as guarded.
1897        * @param doc The styled document where this section placed in.
1898        */

1899        void unmarkGuarded(StyledDocument JavaDoc doc) {
1900            markGuarded(doc, bounds, false);
1901            JavaEditor.this.notifyModified();
1902        }
1903
1904        /** Gets the begin of section. To this position is set the cursor
1905        * when section is open in the editor.
1906        * @return the begin position of section.
1907        */

1908        public PositionRef getBegin() {
1909            return bounds.getBegin();
1910        }
1911
1912        /** Gets the text contained in the section.
1913        * @return The text contained in the section.
1914        */

1915        public String JavaDoc getText() {
1916            StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1917            try {
1918                buf.append(bounds.getText());
1919            }
1920            catch (Exception JavaDoc e) {
1921            }
1922            return buf.toString();
1923        }
1924
1925        /*
1926        public String toString() {
1927          StringBuffer buf = new StringBuffer("SimpleSection:"+name); // NOI18N
1928          buf.append("\"");
1929          try {
1930            buf.append(bounds.getText());
1931          }
1932          catch (Exception e) {
1933            buf.append("EXCEPTION:"); // NOI18N
1934            buf.append(e.getMessage());
1935          }
1936          buf.append("\"");
1937          return buf.toString();
1938    }*/

1939
1940        public PositionRef getPositionAfter() {
1941        return createPositionRef(bounds.getEnd().getOffset(), Position.Bias.Backward);
1942    }
1943
1944        public boolean contains(PositionRef pos,boolean allowHoles) {
1945        return bounds.getBegin().getOffset() <= pos.getOffset() &&
1946        bounds.getEnd().getOffset() >= pos.getOffset();
1947    }
1948
1949        public PositionRef getPositionBefore() {
1950            return createPositionRef(bounds.getBegin().getOffset(), Position.Bias.Forward);
1951    }
1952    }
1953
1954    /** Represents an advanced guarded block.
1955    * It consists of three pieces: a header, body, and footer.
1956    * The header and footer are guarded but the body is not.
1957    */

1958    public final class InteriorSection extends GuardedSection {
1959        /** Text range of the header. */
1960        PositionBounds header;
1961
1962        /** Text range of the header. */
1963        PositionBounds body;
1964
1965        /** Text range of the bottom. */
1966        PositionBounds bottom;
1967
1968        /** Creates new section.
1969        * @param name Name of the new section.
1970        */

1971        InteriorSection(String JavaDoc name, PositionBounds header, PositionBounds body, PositionBounds bottom) {
1972            super(name);
1973            this.header = header;
1974            this.body = body;
1975            this.bottom = bottom;
1976        }
1977
1978        /** Set the text of the body.
1979        * @param text the new text
1980        * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
1981        * @see JavaEditor.GuardedSection#setText
1982        */

1983        public boolean setBody(String JavaDoc text) {
1984            return setText(body, text, false);
1985        }
1986
1987        /** Set the text of the header.
1988        * @param text the new text
1989        * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
1990        * @see JavaEditor.GuardedSection#setText
1991        */

1992        public boolean setHeader(String JavaDoc text) {
1993            return setText(header, text, true);
1994        }
1995
1996        /**
1997         * Returns the contents of the header part of the section. If the
1998         * section is invalid the method returns null.
1999         * @return contents of the header or null, if the section is not valid.
2000         */

2001        public String JavaDoc getHeader() {
2002            if (!isValid())
2003                return null;
2004            try {
2005                return header.getText();
2006            } catch (IOException ex) {
2007                // should not be never reached.
2008
} catch (BadLocationException JavaDoc ex) {
2009                // should not happen.
2010
}
2011            return null;
2012        }
2013
2014        /** Set the text of the bottom.
2015        * Note that the bottom of the section must have exactly one line.
2016        * So, all interior newline characters will be replaced by spaces.
2017        *
2018        * @param text the new text
2019        * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
2020        * @see JavaEditor.GuardedSection#setText
2021        */

2022        public boolean setBottom(String JavaDoc text) {
2023            boolean endsWithEol = text.endsWith("\n"); // NOI18N
2024
int firstEol = text.indexOf('\n');
2025            int lastEol = text.lastIndexOf('\n');
2026
2027            if ((firstEol != lastEol) || (endsWithEol && (firstEol != -1))) {
2028                if (endsWithEol) {
2029                    text = text.substring(0, text.length() - 1);
2030                }
2031                text = text.replace('\n', ' ');
2032            }
2033            return setText(bottom, text, true);
2034        }
2035
2036        /**
2037         * Returns the contents of the bottom part of the guarded section.
2038         * The method will return null, if the section is not valid.
2039         * @return contents of the bottom part, or null if the section is not valid.
2040         */

2041        public String JavaDoc getBottom() throws IOException, BadLocationException JavaDoc {
2042            if (!isValid())
2043                return null;
2044            try {
2045                return bottom.getText();
2046            } catch (IOException ex) {
2047                // should not be never reached.
2048
} catch (BadLocationException JavaDoc ex) {
2049                // should not happen.
2050
}
2051            return null;
2052        }
2053
2054        /** Gets the begin of section. To this position is set the cursor
2055        * when section is open in the editor.
2056        * @return the begin position of the body section - the place where
2057        * is possible to edit.
2058        */

2059        public PositionRef getBegin() {
2060            return body.getBegin();
2061        }
2062
2063        /** Deletes the text in the section.
2064        * @exception BadLocationException
2065        * @exception IOException
2066        */

2067        void deleteText() throws BadLocationException JavaDoc, IOException {
2068            header.setText(""); // NOI18N
2069
body.setText(""); // NOI18N
2070
bottom.setText(""); // NOI18N
2071
deleteNewLineBeforeBlock(header.getBegin().getOffset());
2072        }
2073
2074        /** Marks the section as guarded.
2075        * @param doc The styled document where this section placed in.
2076        */

2077        void markGuarded(StyledDocument JavaDoc doc) {
2078            markGuarded(doc, header, true);
2079            markGuarded(doc, bottom, true);
2080        }
2081
2082        /** Unmarks the section as guarded.
2083        * @param doc The styled document where this section placed in.
2084        */

2085        void unmarkGuarded(StyledDocument JavaDoc doc) {
2086            markGuarded(doc, header, false);
2087            markGuarded(doc, bottom, false);
2088            JavaEditor.this.notifyModified();
2089        }
2090
2091        /** Gets the text contained in the section.
2092        * @return The text contained in the section.
2093        */

2094        public String JavaDoc getText() {
2095            StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2096            try {
2097                buf.append(header.getText());
2098                buf.append(body.getText());
2099                buf.append(bottom.getText());
2100            }
2101            catch (Exception JavaDoc e) {
2102            }
2103            return buf.toString();
2104        }
2105
2106        /*
2107        public String toString() {
2108          StringBuffer buf = new StringBuffer("InteriorSection:"+name); // NOI18N
2109          try {
2110            buf.append("HEADER:\""); // NOI18N
2111            buf.append(header.getText());
2112            buf.append("\"");
2113            buf.append("BODY:\""); // NOI18N
2114            buf.append(body.getText());
2115            buf.append("\"");
2116            buf.append("BOTTOM:\""); // NOI18N
2117            buf.append(bottom.getText());
2118            buf.append("\"");
2119          }
2120          catch (Exception e) {
2121            buf.append("EXCEPTION:"); // NOI18N
2122            buf.append(e.getMessage());
2123          }
2124          return buf.toString();
2125    }*/

2126        public boolean contains(PositionRef pos,boolean allowHoles) {
2127        if (!allowHoles) {
2128            return header.getBegin().getOffset() <= pos.getOffset() &&
2129            bottom.getEnd().getOffset() >= pos.getOffset();
2130        } else {
2131        if (header.getBegin().getOffset() <= pos.getOffset() &&
2132            header.getEnd().getOffset() >= pos.getOffset()) {
2133            return true;
2134        }
2135        return bottom.getBegin().getOffset() <= pos.getOffset() &&
2136            bottom.getEnd().getOffset() >= pos.getOffset();
2137        }
2138    }
2139
2140        public PositionRef getPositionBefore() {
2141        return createPositionRef(header.getBegin().getOffset(), Position.Bias.Forward);
2142    }
2143
2144        public PositionRef getPositionAfter() {
2145        return createPositionRef(bottom.getEnd().getOffset(), Position.Bias.Backward);
2146    }
2147    }
2148
2149    // ==================== Private inner classes ===========================
2150

2151    private class JavaEditorChangeListener implements ChangeListener JavaDoc {
2152        private PropertyChangeListener JavaDoc classpathListener;
2153        private PropertyChangeListener JavaDoc settingListener;
2154
2155        public void stateChanged(ChangeEvent JavaDoc ev) {
2156            JavaSettings js=JavaSettings.getDefault();
2157
2158            // create classpath listener
2159
if (classpathListener == null) {
2160                classpathListener = new PropertyChangeListener JavaDoc() {
2161                    public void propertyChange(PropertyChangeEvent JavaDoc evt) {
2162                        if (ClassPath.PROP_ROOTS.equals(evt.getPropertyName())) {
2163                            classpathChanged();
2164                        }
2165                    }
2166                };
2167            }
2168            if (settingListener==null)
2169                settingListener=new PropertyChangeListener JavaDoc() {
2170                                    public void propertyChange(PropertyChangeEvent JavaDoc evt) {
2171                                        if (JavaSettings.PROP_PARSING_ERRORS.equals(evt.getPropertyName())) {
2172                                            parsingErrorsChanged(evt);
2173                                        }
2174                                        if (JavaSettings.PROP_SHOW_OVERRIDING.equals(evt.getPropertyName())) {
2175                                            showOverridingChanged (evt);
2176                                        }
2177                                    }
2178                                };
2179            ClassPath sourceClasspath = getSourcePath();
2180            ClassPath librariesClasspath = getLibrariesPath();
2181            ClassPath bootClassPath = getBootClassPath();
2182            if (sourceClasspath != null) {
2183                sourceClasspath.removePropertyChangeListener(classpathListener);
2184            }
2185            if (librariesClasspath != null) {
2186                librariesClasspath.removePropertyChangeListener(classpathListener);
2187            }
2188            if (bootClassPath != null) {
2189                bootClassPath.removePropertyChangeListener(classpathListener);
2190            }
2191            js.removePropertyChangeListener(settingListener);
2192            if (isDocumentLoaded()) {
2193                if (sourceClasspath != null) {
2194                    sourceClasspath.addPropertyChangeListener(classpathListener);
2195                }
2196                if (librariesClasspath != null) {
2197                    librariesClasspath.addPropertyChangeListener (classpathListener);
2198                }
2199                if (bootClassPath != null) {
2200                    bootClassPath.addPropertyChangeListener(classpathListener);
2201                }
2202                js.addPropertyChangeListener(settingListener);
2203            }
2204        }
2205
2206    }
2207    /** Comparator of the guarded sections. It compares the begin position
2208    * of the sections.
2209    */

2210    private static class GuardedPositionComparator implements Comparator {
2211        /** Compare two objects. Both have to be either SimpleSection
2212        * either InteriorSection instance.
2213        */

2214        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
2215            return getOffset(o1) - getOffset(o2);
2216        }
2217
2218        /** Computes the offset of the begin of the section. */
2219        private int getOffset(Object JavaDoc o) {
2220            if (o instanceof SimpleSection) {
2221                return ((SimpleSection)o).bounds.getBegin().getOffset();
2222            }
2223            else {
2224                return ((InteriorSection)o).header.getBegin().getOffset();
2225            }
2226        }
2227    }
2228
2229    /** Class for holding information about the one special (guarded)
2230    * comment. It is created by GuardedReader and used by
2231    * JavaEditor to creating the guarded sections.
2232    */

2233    private static class SectionDesc {
2234        /** Type - one of T_XXX constant */
2235        int type;
2236
2237        /** Name of the section comment */
2238        String JavaDoc name;
2239
2240        /** offset of the begin */
2241        int begin;
2242
2243        /** offset of the end */
2244        int end;
2245
2246        /** Simple constructor */
2247        SectionDesc(int type) {
2248            this.type = type;
2249            name = null;
2250            begin = 0;
2251            end = 0;
2252        }
2253    }
2254
2255    /** This stream is able to filter special guarded comments.
2256     * Holding this information is optional and depends on the construction
2257     * of this stream - the reason of this feature is that
2258     * GuardedReader is used also for parser (and it doesn't require
2259     * the storing the guarded block information - just filter the comments).
2260     */

2261    static class GuardedReader extends Reader {
2262        /** Encapsulated reader */
2263        Reader reader;
2264
2265        /** Character buffer */
2266        char[] charBuff;
2267        char[] readBuff;
2268        int howmany;
2269        Pattern JavaDoc magicsAsRE;
2270
2271        /** The flag determining if this stream should store the guarded
2272         * block information (list of the SectionDesc).
2273         */

2274        boolean justFilter;
2275
2276        /** The position at the current line. */
2277        int position;
2278
2279        /** The list of the SectionsDesc. */
2280        LinkedList list;
2281
2282        /** The count of types new line delimiters used in the file */
2283        final int[] newLineTypes;
2284
2285        /** Creates new stream.
2286         * @param is encapsulated input stream.
2287         * @param justFilter The flag determining if this stream should
2288         * store the guarded block information. True means just filter,
2289         * false means store the information.
2290         */

2291        GuardedReader(InputStream is, boolean justFilter) throws IOException {
2292            this(is, justFilter, null);
2293        }
2294
2295        GuardedReader(InputStream is, boolean justFilter, String JavaDoc encoding) throws IOException {
2296            if (encoding == null)
2297                reader = new InputStreamReader(is);
2298            else
2299                reader = new InputStreamReader(is, encoding);
2300            this.justFilter = justFilter;
2301            position = 0;
2302            list = new LinkedList();
2303            newLineTypes = new int[] { 0, 0, 0 };
2304        }
2305
2306        /** Read the array of chars */
2307        public int read(char[] cbuf, int off, int len) throws IOException {
2308
2309            if (charBuff == null) {
2310                readCharBuff();
2311                translateToCharBuff();
2312            }
2313
2314            if (howmany <= 0) {
2315                return -1;
2316            } else {
2317                int min = Math.min(len, howmany);
2318                System.arraycopy(charBuff, position, cbuf, off, min);
2319                howmany -= min;
2320                position += min;
2321                return min;
2322            }
2323        }
2324
2325        /** Reads readBuff */
2326        final void readCharBuff() throws IOException {
2327
2328            char[] tmp = new char[2048];
2329            int read;
2330            ArrayList buffs = new ArrayList(20);
2331
2332            for (;;) {
2333                read = readFully(tmp);
2334                buffs.add(tmp);
2335                if (read < 2048) {
2336                    break;
2337                } else {
2338                    tmp = new char[2048];
2339                }
2340            }
2341
2342            int listsize = buffs.size() - 1;
2343            int size = listsize * 2048 + read;
2344            readBuff = new char[size];
2345            charBuff = new char[size];
2346            int copy = 0;
2347
2348            for (int i = 0; i < listsize; i++) {
2349                char[] tmp2 = (char[]) buffs.get(i);
2350                System.arraycopy(tmp2, 0, readBuff, copy, 2048);
2351                copy += 2048;
2352            }
2353            System.arraycopy(tmp, 0, readBuff, copy, read);
2354        }
2355
2356        /** reads fully given buffer */
2357        final int readFully(final char[] buff) throws IOException {
2358            int read = 0;
2359            int sum = 0;
2360
2361            do {
2362                read = reader.read(buff, sum, buff.length - sum);
2363                sum += read;
2364            } while ((sum < buff.length) && (read > 0));
2365
2366            return sum + 1;
2367        }
2368
2369        /** Called after raw filling from an underlying reader */
2370        final void translateToCharBuff() {
2371            position = 0;
2372
2373            // points to first unused cell in charBuff
2374
int charBuffPtr = 0;
2375            int stop = readBuff.length - 1;
2376
2377            // read char
2378
int c;
2379            // ptr to first not processed char in readBuff
2380
int i = 0;
2381            // points to a character right after a newline
2382
int lastNewLine = 0;
2383
2384            // final automata
2385
int fatpos = 0;
2386            final int MAGICLEN = MAGIC_PREFIX.length();
2387
2388
2389            //process newlines so only '\n' appears in the charBuff
2390
//count all kinds of newlines - most used will be used on save
2391
while (i < stop) {
2392                c = readBuff[i];
2393                switch (c) {
2394                case (int)'\n':
2395                    newLineTypes[NEW_LINE_N]++;
2396                    charBuff[charBuffPtr++] = '\n';
2397                    lastNewLine = charBuffPtr;
2398                    i++;
2399                    break;
2400                case (int)'\r':
2401                    int c2 = readBuff[i + 1];
2402                    if (c2 != (int)'\n') {
2403                        newLineTypes[NEW_LINE_R]++;
2404                        i++;
2405                    } else {
2406                        i +=2;
2407                        newLineTypes[NEW_LINE_RN]++;
2408                    }
2409                    charBuff[charBuffPtr++] = '\n';
2410                    lastNewLine = charBuffPtr;
2411                    break;
2412
2413                default:
2414                    charBuff[charBuffPtr++] = readBuff[i++];
2415                }
2416
2417                switch (fatpos) {
2418                case 0:
2419                    if (c == '/') {
2420                        fatpos++;
2421                    } else {
2422                        fatpos = 0;
2423                    }
2424                    break;
2425
2426                case 1:
2427                    if (c == '/') {
2428                        fatpos++;
2429                    } else {
2430                        fatpos = 0;
2431                    }
2432                    break;
2433
2434                case 2:
2435                    if (c == 'G') {
2436                        fatpos++;
2437            } else if (c == '/') {
2438            fatpos = 2; // what if /////GEN-xxx?
2439
} else {
2440                        fatpos = 0;
2441                    }
2442                    break;
2443
2444                case 3:
2445                    if (c == 'E') {
2446                        fatpos++;
2447                    } else {
2448                        fatpos = 0;
2449                    }
2450                    break;
2451
2452                case 4:
2453                    if (c == 'N') {
2454                        fatpos++;
2455                    } else {
2456                        fatpos = 0;
2457                    }
2458                    break;
2459
2460                case 5:
2461                    if (c == '-') {
2462                        fatpos++;
2463                    } else {
2464                        fatpos = 0;
2465                    }
2466                    break;
2467
2468                default:
2469                    fatpos = 0;
2470                }
2471
2472                // "//GEN-" was reached at this time
2473
if(fatpos == MAGICLEN) {
2474                    fatpos = 0;
2475                    Pattern JavaDoc magics = getMagicsAsRE();
2476                    int searchLen = Math.min(LONGEST_ITEM, readBuff.length - i);
2477                    CharBuffer JavaDoc chi = CharBuffer.wrap(readBuff, i, searchLen);
2478                    Matcher JavaDoc matcher = magics.matcher(chi);
2479                    if (matcher.find()) {
2480                        String JavaDoc match = matcher.group();
2481
2482                        charBuffPtr -= MAGICLEN;
2483                        i += match.length();
2484                        int toNl = toNewLine(i);
2485                        int sectionSize=MAGICLEN+match.length()+toNl;
2486                        
2487                        if (!justFilter) {
2488                            int type = string2Type(match);
2489                            SectionDesc desc = new SectionDesc(type);
2490                            desc.begin = lastNewLine;
2491                            desc.end = charBuffPtr + sectionSize + 1;
2492                            desc.name = new String JavaDoc(readBuff, i, toNl);
2493                            list.add(desc);
2494                        }
2495                        i += toNl;
2496                        Arrays.fill(charBuff,charBuffPtr,charBuffPtr+sectionSize,' ');
2497                        charBuffPtr+=sectionSize;
2498                    }
2499                }
2500            }
2501
2502            if (i == stop) {
2503                c = readBuff[i];
2504                switch (c) {
2505                case (int)'\n':
2506                    newLineTypes[NEW_LINE_N]++;
2507                    charBuff[charBuffPtr++] = '\n';
2508                    break;
2509                case (int)'\r':
2510                    newLineTypes[NEW_LINE_R]++;
2511                    charBuff[charBuffPtr++] = '\n';
2512                    break;
2513
2514                default:
2515                    charBuff[charBuffPtr++] = readBuff[i++];
2516                }
2517            }
2518
2519            // repair last SectionDesc
2520
if (!justFilter && (list.size() > 0)) {
2521                SectionDesc desc = (SectionDesc) list.getLast();
2522                if (desc.end > charBuffPtr) {
2523                    desc.end = charBuffPtr;
2524                }
2525            }
2526
2527            howmany = charBuffPtr;
2528            readBuff = null;
2529        }
2530
2531        /** Translates a String (//GEN-XXX) to its number */
2532        static int string2Type(String JavaDoc match) {
2533            StringBuffer JavaDoc sb = new StringBuffer JavaDoc(MAGIC_PREFIX);
2534            sb.append(match);
2535            match = sb.toString();
2536
2537            final int len = SECTION_MAGICS.length;
2538
2539            for (int i = 0; i < len; i++) {
2540                if (match.equals(SECTION_MAGICS[i])) {
2541                    return i;
2542                }
2543            }
2544            return -1;
2545        }
2546
2547        /** Searches for newline from i */
2548        final int toNewLine(int i) {
2549            int c;
2550            int counter = i;
2551            final int len = readBuff.length;
2552            while (counter < len) {
2553                c = readBuff[counter++];
2554                if (c == '\r' || c == '\n') {
2555                    counter--;
2556                    break;
2557                }
2558            }
2559
2560            return counter - i;
2561        }
2562
2563        /** @return searching engine for magics */
2564        final Pattern JavaDoc getMagicsAsRE() {
2565            if (magicsAsRE == null) {
2566                magicsAsRE = Pattern.compile(makeOrRegexp());
2567            }
2568            return magicsAsRE;
2569        }
2570
2571        /** Makes or regular expression for magics */
2572        final String JavaDoc makeOrRegexp() {
2573            StringBuffer JavaDoc sb = new StringBuffer JavaDoc(100);
2574            final int len = MAGIC_PREFIX.length();
2575            final int slen = SECTION_MAGICS.length - 1;
2576            for (int i = 0; i < slen; i++) {
2577                sb.append(SECTION_MAGICS[i].substring(len));
2578                sb.append('|');
2579            }
2580            sb.append(SECTION_MAGICS[slen].substring(len));
2581            return sb.toString();
2582        }
2583
2584        /** @return most frequently type of new line delimiter */
2585        byte getNewLineType() {
2586            // special case: an empty file (all newline types equal)
2587
if (newLineTypes[0] == newLineTypes[1] &&
2588                newLineTypes[1] == newLineTypes[2]) {
2589
2590                String JavaDoc s = System.getProperty("line.separator");
2591                if ("\r".equals(s)) // NOI18N
2592
return NEW_LINE_R;
2593                else if ("\r\n".equals(s)) // NOI18N
2594
return NEW_LINE_RN;
2595                else
2596                    return NEW_LINE_N;
2597            }
2598            if (newLineTypes[0] > newLineTypes[1]) {
2599                return (newLineTypes[0] > newLineTypes[2]) ? (byte) 0 : 2;
2600            }
2601            else {
2602                return (newLineTypes[1] > newLineTypes[2]) ? (byte) 1 : 2;
2603            }
2604        }
2605
2606        /** Close underlayed writer. */
2607        public void close() throws IOException {
2608            reader.close();
2609        }
2610    }
2611
2612    /** This stream is able to insert special guarded comments.
2613    */

2614    class GuardedWriter extends Writer {
2615        /** Encapsulated writer. */
2616        BufferedWriter writer;
2617
2618        /** From this iterator is possible to obtain all section
2619        * descriptions during writing the document.
2620        */

2621        Iterator sections;
2622
2623        /** Current section from the previous iterator. For filling this
2624        * field is used method nextSection.
2625        */

2626        SectionDesc current;
2627
2628        /** Current offset in the original document (NOT in the encapsulated
2629        * output stream.
2630        */

2631        int offsetCounter;
2632
2633        /** This flag is used during writing. It is complicated to explain. */
2634        boolean wasNewLine;
2635
2636        /** number of consecutive spaces */
2637        int spaces;
2638        
2639        /** Creates new GuardedWriter.
2640        * @param os Encapsulated output stream.
2641        * @param list The list of the guarded sections.
2642        */

2643        GuardedWriter(OutputStream os, ArrayList list, String JavaDoc encoding) throws IOException {
2644            if (encoding == null)
2645                writer = new BufferedWriter(new OutputStreamWriter(os));
2646            else
2647                writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
2648            offsetCounter = 0;
2649            sections = prepareSections(list);
2650            nextSection();
2651            wasNewLine = false;
2652        }
2653
2654        /** Writes chars to underlayed writer */
2655        public void write(char[] cbuf, int off, int len) throws IOException {
2656            for (int i = 0; i < len; i++) {
2657                writeOneChar(cbuf[i + off]);
2658            }
2659        }
2660
2661        /** Calls underlayed writer flush */
2662        public void close() throws IOException {
2663            writer.flush();
2664        }
2665
2666        /** Calls underlayed writer flush */
2667        public void flush() throws IOException {
2668            writer.flush();
2669        }
2670
2671        /** This method prepares the iterator of the SectionDesc classes
2672        * @param list The list of the GuardedSection classes.
2673        * @return iterator of the SectionDesc
2674        */

2675        private Iterator prepareSections(ArrayList list) {
2676            LinkedList dest = new LinkedList();
2677            Collections.sort(list, new GuardedPositionComparator());
2678
2679            Iterator it = list.iterator();
2680            while (it.hasNext()) {
2681                GuardedSection o = (GuardedSection) it.next();
2682                if (o instanceof SimpleSection) {
2683                    SectionDesc desc = new SectionDesc(T_LINE);
2684                    desc.name = o.name;
2685                    desc.begin = ((SimpleSection)o).bounds.getBegin().getOffset();
2686                    desc.end = ((SimpleSection)o).bounds.getEnd().getOffset();
2687                    dest.add(desc);
2688                }
2689                else {
2690                    SectionDesc desc = new SectionDesc(T_HEADER);
2691                    desc.begin = (((InteriorSection)o).header).getBegin().getOffset();
2692                    desc.end = (((InteriorSection)o).header).getEnd().getOffset();
2693                    desc.name = o.name;
2694                    dest.add(desc);
2695
2696                    desc = new SectionDesc(T_END);
2697                    desc.begin = (((InteriorSection)o).bottom).getBegin().getOffset();
2698                    desc.end = (((InteriorSection)o).bottom).getEnd().getOffset();
2699                    desc.name = o.name;
2700                    dest.add(desc);
2701                }
2702            }
2703            return dest.iterator();
2704        }
2705
2706        /** Write one character. If there is a suitable place,
2707        * some special comments are written to the underlaying stream.
2708        * @param b char to write.
2709        */

2710        void writeOneChar(int b) throws IOException {
2711            if (b == '\r')
2712                return;
2713
2714            if (current != null) {
2715                if (offsetCounter == current.begin) {
2716                    wasNewLine = false;
2717                }
2718                if ((b == '\n') && (current.begin <= offsetCounter)) {
2719                    switch (current.type) {
2720                    case T_LINE:
2721                        if (!wasNewLine) {
2722                            if (offsetCounter + 1 >= current.end) {
2723                                writeMagic(T_LINE, current.name);
2724                                nextSection();
2725                            }
2726                            else {
2727                                writeMagic(T_BEGIN, current.name);
2728                                wasNewLine = true;
2729                            }
2730                        }
2731                        else {
2732                            if (offsetCounter + 1 >= current.end) {
2733                                writeMagic(T_END, current.name);
2734                                nextSection();
2735                            }
2736                        }
2737                        break;
2738
2739                    case T_HEADER:
2740                        if (!wasNewLine) {
2741                            if (offsetCounter + 1 >= current.end) {
2742                                writeMagic(T_FIRST, current.name);
2743                                nextSection();
2744                            }
2745                            else {
2746                                writeMagic(T_FIRST, current.name);
2747                                wasNewLine = true;
2748                            }
2749                        }
2750                        else {
2751                            if (offsetCounter + 1 >= current.end) {
2752                                writeMagic(T_HEADEREND, current.name);
2753                                nextSection();
2754                            }
2755                        }
2756                        break;
2757
2758                    case T_END:
2759                        writeMagic(T_LAST, current.name);
2760                        nextSection();
2761                        break;
2762                    }
2763                }
2764            }
2765            if (b==' ')
2766                spaces++;
2767            else {
2768                char[] sp=new char[spaces];
2769                
2770                Arrays.fill(sp,' ');
2771                writer.write(sp);
2772                writer.write(b);
2773                spaces=0;
2774            }
2775            offsetCounter++;
2776        }
2777
2778        /** Try to get next sectionDesc from the 'sections'
2779        * If there is no more section the 'current' will be set to null.
2780        */

2781        private void nextSection() {
2782            current = (SectionDesc) (sections.hasNext() ? sections.next() : null);
2783        }
2784
2785        /** Writes the magic to the underlaying stream.
2786        * @param type The type of the magic section - T_XXX constant.
2787        * @param name name of the section.
2788        */

2789        private void writeMagic(int type, String JavaDoc name) throws IOException {
2790            if (!shouldReload) {
2791                shouldReload = spaces != SECTION_MAGICS[type].length() + name.length();
2792            }
2793            spaces=0;
2794            writer.write(SECTION_MAGICS[type], 0, SECTION_MAGICS[type].length());
2795            writer.write(name, 0, name.length());
2796        }
2797    }
2798
2799    /** This stream is used for changing the new line delimiters.
2800    * It replaces the '\n' by '\n', '\r' or "\r\n"
2801    */

2802    private static class NewLineOutputStream extends OutputStream {
2803        /** Underlaying stream. */
2804        OutputStream stream;
2805
2806        /** The type of new line delimiter */
2807        byte newLineType;
2808
2809        /** Creates new stream.
2810        * @param stream Underlaying stream
2811        * @param newLineType The type of new line delimiter
2812        */

2813        public NewLineOutputStream(OutputStream stream, byte newLineType) {
2814            this.stream = stream;
2815            this.newLineType = newLineType;
2816        }
2817
2818        /** Write one character.
2819        * @param b char to write.
2820        */

2821        public void write(int b) throws IOException {
2822            if (b == '\n') {
2823                switch (newLineType) {
2824                case NEW_LINE_R:
2825                    stream.write('\r');
2826                    break;
2827                case NEW_LINE_RN:
2828                    stream.write('\r');
2829                case NEW_LINE_N:
2830                    stream.write('\n');
2831                    break;
2832                }
2833            }
2834            else {
2835                stream.write(b);
2836            }
2837        }
2838    }
2839
2840    static class WParsingListener extends WeakReference JavaDoc implements ParsingListener, Runnable JavaDoc {
2841        WParsingListener(ParsingListener orig) {
2842            super(orig, Utilities.activeReferenceQueue());
2843        }
2844
2845        public void run() {
2846            JavaMetamodel.removeParsingListener(this);
2847        }
2848
2849        ParsingListener getListener() {
2850            Object JavaDoc o = get();
2851            if (o == null) {
2852                JavaMetamodel.removeParsingListener(this);
2853            }
2854            return (ParsingListener) o;
2855        }
2856
2857        public void resourceParsed(Resource rsc) {
2858            ParsingListener l = getListener();
2859            if (l != null)
2860                l.resourceParsed(rsc);
2861        }
2862    }
2863
2864    private static class UndoManagerListener implements PropertyChangeListener JavaDoc {
2865        
2866        private class ActiveQueueReference extends WeakReference JavaDoc implements Runnable JavaDoc {
2867            public ActiveQueueReference(Object JavaDoc o, ReferenceQueue JavaDoc q) {
2868                super(o, q);
2869            }
2870            
2871            public void run() {
2872                UndoManager undo = JavaMetamodel.getUndoManager();
2873                undo.removePropertyChangeListener(UndoManagerListener.this);
2874            }
2875        }
2876        
2877        private WeakReference JavaDoc ref;
2878        
2879        UndoManagerListener(JavaEditor editor) {
2880            ref = new ActiveQueueReference(editor, Utilities.activeReferenceQueue());
2881        }
2882        
2883        public void propertyChange(PropertyChangeEvent JavaDoc evt) {
2884            UndoManager undo = JavaMetamodel.getUndoManager();
2885            JavaEditor editor = (JavaEditor) ref.get();
2886            if (editor == null) {
2887                undo.removePropertyChangeListener(this);
2888                return;
2889            }
2890            if (undo.isUndoAvailable() || undo.isRedoAvailable()) {
2891                editor.getUndoRedo().discardAllEdits();
2892            }
2893        }
2894    }
2895}
2896
Popular Tags