KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > api > retouche > source > Source


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

19
20 package org.netbeans.api.retouche.source;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.io.OutputStreamWriter JavaDoc;
29 import java.io.PrintWriter JavaDoc;
30 import java.lang.ref.Reference JavaDoc;
31 import java.lang.ref.WeakReference JavaDoc;
32 import java.lang.reflect.Method JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Collection JavaDoc;
36 import java.util.Collections JavaDoc;
37 import java.util.Comparator JavaDoc;
38 import java.util.HashMap JavaDoc;
39 import java.util.HashSet JavaDoc;
40 import java.util.Iterator JavaDoc;
41 import java.util.LinkedList JavaDoc;
42 import java.util.List JavaDoc;
43 import java.util.Map JavaDoc;
44 import java.util.WeakHashMap JavaDoc;
45 import java.util.concurrent.Executors JavaDoc;
46 import java.util.concurrent.PriorityBlockingQueue JavaDoc;
47 import java.util.concurrent.ThreadFactory JavaDoc;
48 import java.util.concurrent.TimeUnit JavaDoc;
49 import java.util.concurrent.atomic.AtomicBoolean JavaDoc;
50 import java.util.concurrent.locks.ReentrantLock JavaDoc;
51 import java.util.logging.Level JavaDoc;
52 import java.util.logging.Logger JavaDoc;
53 import javax.swing.event.ChangeEvent JavaDoc;
54 import javax.swing.event.ChangeListener JavaDoc;
55 import javax.swing.event.DocumentEvent JavaDoc;
56 import javax.swing.event.DocumentListener JavaDoc;
57 import javax.swing.text.AbstractDocument JavaDoc;
58 import javax.swing.text.Document JavaDoc;
59 import javax.swing.text.StyledDocument JavaDoc;
60 import javax.swing.event.CaretEvent JavaDoc;
61 import javax.swing.event.CaretListener JavaDoc;
62 import javax.swing.event.ChangeListener JavaDoc;
63 import javax.swing.text.JTextComponent JavaDoc;
64 import org.netbeans.api.gsf.ParserFile;
65 import org.netbeans.api.java.classpath.ClassPath;
66 import org.netbeans.api.java.platform.JavaPlatformManager;
67 import org.netbeans.api.java.queries.SourceLevelQuery;
68 import org.netbeans.api.gsf.Error;
69 import org.netbeans.api.gsf.ParseEvent;
70 import org.netbeans.api.gsf.ParseListener;
71 import org.netbeans.api.gsf.Parser;
72 import org.netbeans.api.gsf.ParserResult;
73 import org.netbeans.api.gsf.SourceFileReader;
74 import org.netbeans.api.gsf.CancellableTask;
75 import org.netbeans.api.retouche.source.ClasspathInfo.PathKind;
76 import org.netbeans.api.retouche.source.CompilationUnitTree;
77 import org.netbeans.api.retouche.source.ModificationResult.Difference;
78 import org.netbeans.api.retouche.source.ParserTaskImpl;
79 import org.netbeans.api.retouche.source.support.CaretAwareSourceTaskFactory;
80 //import org.netbeans.api.timers.TimesCollector;
81
import org.netbeans.editor.Registry;
82 import org.netbeans.modules.gsf.Language;
83 import org.netbeans.modules.gsf.LanguageRegistry;
84 import org.netbeans.modules.retouche.source.SourceAccessor;
85 import org.netbeans.modules.retouche.source.parsing.SourceFileObject;
86 import org.netbeans.modules.retouche.source.usages.ClassIndexImpl;
87 import org.netbeans.modules.retouche.source.usages.ClassIndexManager;
88 import org.netbeans.modules.retouche.source.util.LowMemoryEvent;
89 import org.netbeans.modules.retouche.source.util.LowMemoryListener;
90 import org.netbeans.modules.retouche.source.util.LowMemoryNotifier;
91 import org.netbeans.spi.gsf.DefaultParserFile;
92 import org.openide.cookies.EditorCookie;
93 import org.openide.filesystems.FileChangeAdapter;
94 import org.openide.filesystems.FileChangeListener;
95 import org.openide.filesystems.FileEvent;
96 import org.openide.filesystems.FileObject;
97 import org.openide.filesystems.FileRenameEvent;
98 import org.openide.filesystems.FileUtil;
99 import org.openide.filesystems.FileUtil;
100 import org.openide.loaders.DataObject;
101 import org.openide.loaders.DataObjectNotFoundException;
102 import org.openide.util.Exceptions;
103 import org.openide.util.NbBundle;
104 import org.openide.util.RequestProcessor;
105 import org.openide.util.WeakListeners;
106
107
108 /**
109  * This file is originally from Retouche, the Java Support
110  * infrastructure in NetBeans. I have modified the file as little
111  * as possible to make merging Retouche fixes back as simple as
112  * possible.
113  *
114  *
115  * This file is based on the JavaSource class in Retouche's org.netbeans.api.java.source package.
116  * It represents an open source file in the editor.
117  *
118  * @author Petr Hrebejk
119  * @author Tomas Zezula
120  * @author Tor Norbye
121  */

122 public final class Source {
123
124     public static enum Priority {
125         MAX,
126         HIGH,
127         ABOVE_NORMAL,
128         NORMAL,
129         BELOW_NORMAL,
130         LOW,
131         MIN
132     }
133
134     /**
135      * This specialization of {@link IOException} signals that a {@link Source#runUserActionTask}
136      * or {@link Source#runModificationTask} failed due to lack of memory. The {@link InsufficientMemoryException#getFile}
137      * method returns a file which cannot be processed.
138      */

139     public static final class InsufficientMemoryException extends IOException JavaDoc {
140
141         private FileObject fo;
142
143         private InsufficientMemoryException (final String JavaDoc message, final FileObject fo) {
144             super (message);
145             this.fo = fo;
146         }
147
148         private InsufficientMemoryException (FileObject fo) {
149             this (NbBundle.getMessage(Source.class, "MSG_UnsufficientMemoryException", FileUtil.getFileDisplayName (fo)),fo);
150         }
151
152
153         /**
154          * Returns file which cannot be processed due to lack of memory.
155          * @return {@link FileObject}
156          */

157         public FileObject getFile () {
158             return this.fo;
159         }
160     }
161
162     /**
163      * Constants for Source.flags
164      */

165     private static final int INVALID = 1;
166     private static final int CHANGE_EXPECTED = INVALID<<1;
167     private static final int RESCHEDULE_FINISHED_TASKS = CHANGE_EXPECTED<<1;
168     private static final int UPDATE_INDEX = RESCHEDULE_FINISHED_TASKS<<1;
169     
170     /**Slow task reporting*/
171     private static final boolean reportSlowTasks = Boolean.getBoolean("org.netbeans.api.retouche.source.Source.reportSlowTasks"); //NOI18N
172
/**Limit for task to be marked as a slow one, in ms*/
173     private static final int SLOW_TASK_LIMIT = 250;
174     private static final int SLOW_CANCEL_LIMIT = 50;
175
176     /**Not final for tests.*/
177     static int REPARSE_DELAY = 500;
178
179     /**
180      * Helper maps mapping the {@link Phase} to key and message for
181      * the {@link TimesCollector}
182      */

183     private static Map JavaDoc<Phase, String JavaDoc> phase2Key = new HashMap JavaDoc<Phase, String JavaDoc> ();
184     private static Map JavaDoc<Phase, String JavaDoc> phase2Message = new HashMap JavaDoc<Phase, String JavaDoc> ();
185
186     /**
187      * Init the maps
188      */

189     static {
190         SourceAccessor.INSTANCE = new JavaSourceAccessorImpl ();
191         phase2Key.put(Phase.PARSED,"parsed"); //NOI18N
192
phase2Message.put (Phase.PARSED,"Parsed"); //NOI18N
193

194         phase2Key.put(Phase.ELEMENTS_RESOLVED,"sig-attributed"); //NOI18N
195
phase2Message.put (Phase.ELEMENTS_RESOLVED,"Signatures Attributed"); //NOI18N
196

197         phase2Key.put(Phase.RESOLVED, "attributed"); //NOI18N
198
phase2Message.put (Phase.RESOLVED, "Attributed"); //NOI18N
199

200     }
201
202     private final static PriorityBlockingQueue JavaDoc<Request> requests = new PriorityBlockingQueue JavaDoc<Request> (10, new RequestComparator());
203     private final static Map JavaDoc<Source, Collection JavaDoc<Request>> finishedRequests = new WeakHashMap JavaDoc<Source,Collection JavaDoc<Request>>();
204     private final static Map JavaDoc<Source,Collection JavaDoc<Request>> waitingRequests = new WeakHashMap JavaDoc<Source,Collection JavaDoc<Request>>();
205     private final static Collection JavaDoc<CancellableTask> toRemove = new LinkedList JavaDoc<CancellableTask> ();
206     private final static SingleThreadFactory factory = new SingleThreadFactory ();
207     private final static CurrentRequestReference currentRequest = new CurrentRequestReference ();
208     private final static EditorRegistryListener editorRegistryListener = new EditorRegistryListener ();
209     //Only single thread can operate on the single javac
210
private final static ReentrantLock JavaDoc javacLock = new ReentrantLock JavaDoc (true);
211
212     private final Collection JavaDoc<FileObject> files;
213     private final FileObject rootFo;
214     private final FileChangeListener fileChangeListener;
215     private DocListener listener;
216     private DataObjectListener dataObjectListener;
217     private String JavaDoc sourceLevel;
218
219     private final ClasspathInfo classpathInfo;
220     private CompilationInfo currentInfo;
221     private java.util.Stack JavaDoc<CompilationInfo> infoStack = new java.util.Stack JavaDoc<CompilationInfo> ();
222
223     private int flags = 0;
224
225     static {
226         Executors.newSingleThreadExecutor(factory).submit (new CompilationJob());
227     }
228
229
230     /**
231      * Returns a {@link Source} instance representing given {@link org.openide.filesystems.FileObject}s
232      * and classpath represented by given {@link ClasspathInfo}.
233      *
234      *
235      * @param cpInfo the classpaths to be used.
236      * @param files for which the {@link Source} should be created
237      * @return a new {@link Source}
238      * @throws {@link IllegalArgumentException} if fileObject or cpInfo is null
239      */

240     public static Source create(final ClasspathInfo cpInfo, final Collection JavaDoc<? extends FileObject> files) throws IllegalArgumentException JavaDoc {
241         if (files == null || cpInfo == null) {
242             throw new IllegalArgumentException JavaDoc ();
243         }
244         try {
245             return new Source(cpInfo, files);
246         } catch (DataObjectNotFoundException donf) {
247             Logger.getLogger("global").warning("Ignoring non existent file: " + FileUtil.getFileDisplayName(donf.getFileObject())); //NOI18N
248
} catch (IOException JavaDoc ex) {
249             Exceptions.printStackTrace(ex);
250         }
251         return null;
252     }
253
254
255     /**
256      * Returns a {@link Source} instance representing given {@link org.openide.filesystems.FileObject}s
257      * and classpath represented by given {@link ClasspathInfo}.
258      *
259      *
260      * @param cpInfo the classpaths to be used.
261      * @param files for which the {@link Source} should be created
262      * @return a new {@link Source}
263      * @throws {@link IllegalArgumentException} if fileObject or cpInfo is null
264      */

265     public static Source create(final ClasspathInfo cpInfo, final FileObject... files) throws IllegalArgumentException JavaDoc {
266         if (files == null || cpInfo == null) {
267             throw new IllegalArgumentException JavaDoc ();
268         }
269         return create(cpInfo, Arrays.asList(files));
270     }
271
272     private static Map JavaDoc<FileObject, Reference JavaDoc<Source>> file2JavaSource = new WeakHashMap JavaDoc<FileObject, Reference JavaDoc<Source>>();
273
274     /**
275      * Returns a {@link Source} instance associated to given {@link org.openide.filesystems.FileObject},
276      * it returns null if the {@link Document} is not associanted with data type providing the {@link Source}.
277      *
278      *
279      * @param fileObject for which the {@link Source} should be found/created.
280      * @return {@link Source} or null
281      * @throws {@link IllegalArgumentException} if fileObject is null
282      */

283     public static Source forFileObject(FileObject fileObject) throws IllegalArgumentException JavaDoc {
284         if (fileObject == null) {
285             throw new IllegalArgumentException JavaDoc ("fileObject == null"); //NOI18N
286
}
287         if (!fileObject.isValid()) {
288             return null;
289         }
290 // if (!"text/x-java".equals(FileUtil.getMIMEType(fileObject)) && !"java".equals(fileObject.getExt())) { //NOI18N
291
// //TODO: Source cannot be created for all kinds of files, but text/x-java is too restrictive:
292
// return null;
293
// }
294
if (!LanguageRegistry.getInstance().isSupported(fileObject.getMIMEType())) {
295             return null;
296         }
297
298         Reference JavaDoc<Source> ref = file2JavaSource.get(fileObject);
299         Source js = ref != null ? ref.get() : null;
300
301         if (js == null) {
302             file2JavaSource.put(fileObject, new WeakReference JavaDoc(js = create(ClasspathInfo.create(fileObject), fileObject)));
303         }
304
305         return js;
306     }
307
308     /**
309      * Returns a {@link Source} instance associated to {@link org.openide.filesystems.FileObject}
310      * the {@link Document} was created from, it returns null if the {@link Document} is not
311      * associanted with data type providing the {@link Source}.
312      *
313      *
314      * @param doc {@link Document} for which the {@link Source} should be found/created.
315      * @return {@link Source} or null
316      * @throws {@link IllegalArgumentException} if doc is null
317      */

318     public static Source forDocument(Document JavaDoc doc) throws IllegalArgumentException JavaDoc {
319         if (doc == null) {
320             throw new IllegalArgumentException JavaDoc ("doc == null"); //NOI18N
321
}
322         Reference JavaDoc<Source> ref = (Reference JavaDoc<Source>)doc.getProperty(Source.class);
323         Source js = ref != null ? ref.get() : null;
324         if (js == null) {
325             DataObject dObj = (DataObject)doc.getProperty(Document.StreamDescriptionProperty);
326             if (dObj != null)
327                 js = forFileObject(dObj.getPrimaryFile());
328         }
329         return js;
330     }
331     
332
333     /**
334      * Creates a new instance of Source
335      *
336      *
337      * @param files to create Source for
338      * @param cpInfo classpath info
339      */

340     private Source (ClasspathInfo cpInfo, Collection JavaDoc<? extends FileObject> files) throws IOException JavaDoc {
341         this.files = Collections.unmodifiableList(new ArrayList JavaDoc (files)); //Create a defensive copy, prevent modification
342
this.fileChangeListener = new FileChangeListenerImpl ();
343         boolean multipleSources = this.files.size() > 1;
344         for (Iterator JavaDoc<? extends FileObject> it = this.files.iterator(); it.hasNext();) {
345             FileObject file = it.next();
346             try {
347                 //TimesCollector.getDefault().reportReference( file, Source.class.toString(), "[M] Source", this ); //NOI18N
348
if (!multipleSources) {
349                     file.addFileChangeListener(FileUtil.weakFileChangeListener(this.fileChangeListener,file));
350                     this.assignDocumentListener(file);
351                     this.dataObjectListener = new DataObjectListener(file);
352                 }
353             } catch (DataObjectNotFoundException donf) {
354                 if (multipleSources) {
355                     Logger.getLogger("global").warning("Ignoring non existent file: " + FileUtil.getFileDisplayName(file)); //NOI18N
356
it.remove();
357                 }
358                 else {
359                     throw donf;
360                 }
361             }
362         }
363         this.classpathInfo = cpInfo;
364         if (files.size() == 1) {
365             this.rootFo = classpathInfo.getClassPath(PathKind.SOURCE).findOwnerRoot(files.iterator().next());
366         }
367         else {
368             this.rootFo = null;
369         }
370         this.classpathInfo.addChangeListener(WeakListeners.change(this.listener, this.classpathInfo));
371         
372         
373     }
374
375     /** Runs a task which permits for controlling phases of the parsing process.
376      * You probably do not want to call this method unless you are reacting to
377      * some user's GUI input which requires immediate action (e.g. code completion popup).
378      * In all other cases use {@link JavaSourceTaskFactory}.<BR>
379      * Call to this method will cancel processing of all the phase completion tasks until
380      * this task does not finish.<BR>
381      * @see org.netbeans.api.retouche.source.CancellableTask for information about implementation requirements
382      * @param task The task which.
383      * @param shared if true the java compiler may be reused by other {@link org.netbeans.api.retouche.source.CancellableTasks},
384      * the value false may have negative impact on the IDE performance.
385      */

386     public void runUserActionTask( final CancellableTask<CompilationController> task, final boolean shared) throws IOException JavaDoc {
387         if (task == null) {
388             throw new IllegalArgumentException JavaDoc ("Task cannot be null"); //NOI18N
389
}
390
391         assert !holdsDocumentWriteLock(files) : "Source.runCompileControlTask called under Document write lock."; //NOI18N
392

393         if (this.files.size()<=1) {
394             CompilationInfo currentInfo = null;
395             synchronized (this) {
396                 if (this.currentInfo != null && (this.flags & INVALID)==0) {
397                     currentInfo = this.currentInfo;
398                 }
399                 if (!shared) {
400                     this.flags|=INVALID;
401                 }
402             }
403             if (currentInfo == null) {
404                 currentInfo = createCurrentInfo(this,this.files.isEmpty() ? null : this.files.iterator().next(), null);
405                 if (shared) {
406                     synchronized (this) {
407                         if (this.currentInfo == null || (this.flags & INVALID) != 0) {
408                             this.currentInfo = currentInfo;
409                             this.flags&=~INVALID;
410                         }
411                         else {
412                             currentInfo = this.currentInfo;
413                         }
414                     }
415                 }
416             }
417             assert currentInfo != null;
418             final Source.Request request = currentRequest.getTaskToCancel();
419             try {
420                 if (request != null) {
421                     request.task.cancel();
422                 }
423                 this.javacLock.lock();
424                 try {
425                     if (shared) {
426                         if (!infoStack.isEmpty()) {
427                             currentInfo = infoStack.peek();
428                         }
429                     }
430                     else {
431                         infoStack.push (currentInfo);
432                     }
433                     try {
434                         task.run (new CompilationController (currentInfo));
435                     } finally {
436                         if (!shared) {
437                             infoStack.pop ();
438                         }
439                     }
440                 } catch (Exception JavaDoc e) {
441                     IOException JavaDoc ioe = new IOException JavaDoc ();
442                     ioe.initCause(e);
443                     throw ioe;
444                 } finally {
445                     this.javacLock.unlock();
446                 }
447             } finally {
448                 currentRequest.cancelCompleted (request);
449             }
450         }
451         else {
452             final Source.Request request = currentRequest.getTaskToCancel();
453             try {
454                 if (request != null) {
455                     request.task.cancel();
456                 }
457                 this.javacLock.lock();
458                 try {
459                     ParserTaskImpl jt = null;
460                     FileObject activeFile = null;
461                     Iterator JavaDoc<FileObject> files = this.files.iterator();
462                     while (files.hasNext() || activeFile != null) {
463                         boolean restarted;
464                         if (activeFile == null) {
465                             activeFile = files.next();
466                             restarted = false;
467                         }
468                         else {
469                             restarted = true;
470                         }
471                         CompilationInfo ci = createCurrentInfo(this,activeFile,jt);
472                         task.run(new CompilationController(ci));
473                         if (!ci.needsRestart) {
474                             jt = ci.getParserTask();
475 // Log.instance(jt.getContext()).nerrors = 0;
476
activeFile = null;
477                         }
478                         else {
479                             jt = null;
480                             ci = null;
481                             System.gc();
482                             if (restarted) {
483                                 throw new InsufficientMemoryException (activeFile);
484                             }
485                         }
486                     }
487                 } catch (Exception JavaDoc e) {
488                     IOException JavaDoc ioe = new IOException JavaDoc ();
489                     ioe.initCause(e);
490                     throw ioe;
491                 } finally {
492                     this.javacLock.unlock();
493                 }
494             } finally {
495                 currentRequest.cancelCompleted(request);
496             }
497         }
498     }
499
500     /** Runs a task which permits for modifying the sources.
501      * Call to this method will cancel processig of all the phase completion tasks until
502      * this task does not finish.<BR>
503      * @see CancellableTask for information about implementation requirements
504      * @param task The task which.
505      */

506     public ModificationResult runModificationTask(CancellableTask<WorkingCopy> task) throws IOException JavaDoc {
507         if (task == null) {
508             throw new IllegalArgumentException JavaDoc ("Task cannot be null"); //NOI18N
509
}
510
511         assert !holdsDocumentWriteLock(files) : "Source.runModificationTask called under Document write lock."; //NOI18N
512

513         ModificationResult result = new ModificationResult();
514         if (this.files.size()<=1) {
515             CompilationInfo currentInfo = null;
516             synchronized (this) {
517                 if (this.currentInfo != null && (this.flags & INVALID) == 0) {
518                     currentInfo = this.currentInfo;
519                 }
520             }
521             if (currentInfo == null) {
522                 currentInfo = createCurrentInfo(this,this.files.isEmpty() ? null : this.files.iterator().next(), null);
523                 synchronized (this) {
524                     if (this.currentInfo == null || (this.flags & INVALID) != 0) {
525                         this.currentInfo = currentInfo;
526                         this.flags&=~INVALID;
527                     }
528                     else {
529                         currentInfo = this.currentInfo;
530                     }
531                 }
532             }
533             assert currentInfo != null;
534             final Source.Request request = currentRequest.getTaskToCancel();
535             try {
536                 if (request != null) {
537                     request.task.cancel();
538                 }
539                 this.javacLock.lock();
540                 try {
541                     WorkingCopy copy = new WorkingCopy (currentInfo);
542                     task.run (copy);
543                     List JavaDoc<Difference> diffs = copy.getChanges();
544                     if (diffs != null && diffs.size() > 0)
545                         result.diffs.put(currentInfo.getFileObject(), diffs);
546                 } catch (Exception JavaDoc e) {
547                     IOException JavaDoc ioe = new IOException JavaDoc ();
548                     ioe.initCause(e);
549                     throw ioe;
550                 } finally {
551                     this.javacLock.unlock();
552                 }
553             } finally {
554                 currentRequest.cancelCompleted (request);
555             }
556         }
557         else {
558             final Source.Request request = currentRequest.getTaskToCancel();
559             try {
560                 if (request != null) {
561                     request.task.cancel();
562                 }
563                 this.javacLock.lock();
564                 try {
565                     ParserTaskImpl jt = null;
566                     FileObject activeFile = null;
567                     Iterator JavaDoc<FileObject> files = this.files.iterator();
568                     while (files.hasNext() || activeFile != null) {
569                         boolean restarted;
570                         if (activeFile == null) {
571                             activeFile = files.next();
572                             restarted = false;
573                         }
574                         else {
575                             restarted = true;
576                         }
577                         CompilationInfo ci = createCurrentInfo(this,activeFile,jt);
578                         WorkingCopy copy = new WorkingCopy(ci);
579                         task.run(copy);
580                         if (!ci.needsRestart) {
581                             jt = ci.getParserTask();
582 // Log.instance(jt.getContext()).nerrors = 0;
583
List JavaDoc<Difference> diffs = copy.getChanges();
584                             if (diffs != null && diffs.size() > 0)
585                                 result.diffs.put(ci.getFileObject(), diffs);
586                             activeFile = null;
587                         }
588                         else {
589                             jt = null;
590                             ci = null;
591                             System.gc();
592                             if (restarted) {
593                                 throw new InsufficientMemoryException (activeFile);
594                             }
595                         }
596                     }
597                 } catch (Exception JavaDoc e) {
598                     IOException JavaDoc ioe = new IOException JavaDoc ();
599                     ioe.initCause(e);
600                     throw ioe;
601                 } finally {
602                     this.javacLock.unlock();
603                 }
604             } finally {
605                 currentRequest.cancelCompleted(request);
606             }
607         }
608         return result;
609     }
610
611     /** Adds a task to given compilation phase. The tasks will run sequentially by
612      * priorty after given phase is reached.
613      * @see CancellableTask for information about implementation requirements
614      * @task The task to run.
615      * @phase In which phase should the task run
616      * @priority Priority of the task.
617      */

618     void addPhaseCompletionTask( CancellableTask<CompilationInfo> task, Phase phase, Priority priority ) throws IOException JavaDoc {
619         if (task == null) {
620             throw new IllegalArgumentException JavaDoc ("Task cannot be null"); //NOI18N
621
}
622         if (phase == null || phase == Phase.MODIFIED) {
623             throw new IllegalArgumentException JavaDoc (String.format("The %s is not a legal value of phase",phase)); //NOI18N
624
}
625         if (priority == null) {
626             throw new IllegalArgumentException JavaDoc ("The priority cannot be null"); //NOI18N
627
}
628         CompilationInfo currentInfo;
629         synchronized (this) {
630             currentInfo = this.currentInfo;
631         }
632         if (currentInfo == null) {
633             currentInfo = createCurrentInfo (this, this.files.isEmpty() ? null : this.files.iterator().next(), null);
634         }
635         synchronized (this) {
636             if (this.currentInfo == null) {
637                 this.currentInfo = currentInfo;
638             }
639         }
640         handleAddRequest (new Request (task, this, phase, priority, true));
641     }
642
643     /** Removes the task from the phase queue.
644      * @task The task to remove.
645      */

646     void removePhaseCompletionTask( CancellableTask<CompilationInfo> task ) {
647         synchronized (Source.class) {
648             toRemove.add (task);
649         }
650     }
651
652     /**Rerun the task in case it was already run. Does nothing if the task was not already run.
653      *
654      * @task to reschedule
655      */

656     void rescheduleTask(CancellableTask<CompilationInfo> task) {
657         synchronized (Source.class) {
658             Source.Request request = this.currentRequest.getTaskToCancel (task);
659             if ( request == null) {
660 out: for (Iterator JavaDoc<Collection JavaDoc<Request>> it = finishedRequests.values().iterator(); it.hasNext();) {
661                     Collection JavaDoc<Request> cr = it.next ();
662                     for (Iterator JavaDoc<Request> it2 = cr.iterator(); it2.hasNext();) {
663                         Request fr = it2.next();
664                         if (task == fr.task) {
665                             it2.remove();
666                             Source.requests.add(fr);
667                             if (cr.size()==0) {
668                                 it.remove();
669                             }
670                             break out;
671                         }
672                     }
673                 }
674             }
675             else {
676                 currentRequest.cancelCompleted(request);
677             }
678         }
679     }
680
681     /**
682      * Marks this {@link Source} as modified, causes that the cached information are
683      * cleared and all the PhaseCompletionTasks are restarted.
684      * The only client of this method should be the JavaDataObject or other DataObjects
685      * providing the {@link Source}. If you call this method in another case you are
686      * probably doing something incorrect.
687      */

688     void revalidate () {
689         this.resetState(true, false);
690     }
691
692     /**
693      * Returns the classpaths ({@link ClasspathInfo}) used by this
694      * {@link Source}
695      *
696      *
697      * @return {@link ClasspathInfo}, never returns null.
698      */

699     public ClasspathInfo getClasspathInfo() {
700         return classpathInfo;
701     }
702
703     /**
704      * Returns unmodifiable {@link Collection} of {@link FileObject}s used by this {@link JavaSource}
705      * @return the {@link FileObject}s
706      */

707     public Collection JavaDoc<FileObject> getFileObjects() {
708         return files;
709     }
710
711     ParserTaskImpl createParserTask(/*final DiagnosticListener<? super SourceFileObject> diagnosticListener,*/ CompilationInfo compilationInfo) {
712         //assert diagnosticListener == null;
713
Language language = LanguageRegistry.getInstance().getLanguageByMimeType(compilationInfo.getFileObject().getMIMEType());
714         assert language != null;
715         ParserTaskImpl javacTask = createParserTask(language, compilationInfo, getClasspathInfo(), /*diagnosticListener,*/ sourceLevel, false);
716 // Context context = javacTask.getContext();
717
// Messager.preRegister(context, null);
718
// ErrorHandlingJavadocEnter.preRegister(context);
719
// JavadocMemberEnter.preRegister(context);
720
// SourceUtils.JavaDocEnv.preRegister(context, getClasspathInfo());
721
// Scanner.Factory.instance(context);
722
// Builder2.instance(context).keepComments = true;
723
return javacTask;
724     }
725
726     private static ParserTaskImpl createParserTask(Language language, final CompilationInfo currentInfo, final ClasspathInfo cpInfo, /*final DiagnosticListener<? super SourceFileObject> diagnosticListener,*/ final String JavaDoc sourceLevel, final boolean backgroundCompilation) {
727         ArrayList JavaDoc<String JavaDoc> options = new ArrayList JavaDoc<String JavaDoc>();
728
729
730         // CompilationUnitTree unit = new CompilationUnitTree();
731

732         Parser parser = language.getParser();
733         assert parser != null;
734         ParserTaskImpl jti = new ParserTaskImpl(language);
735         jti.setParser(parser);
736         if (currentInfo != null) {
737             currentInfo.setParser(parser);
738         }
739
740         return jti;
741     }
742
743
744     /**
745      * Not synchronized, only the CompilationJob's thread can call it!!!!
746      *
747      */

748     static Phase moveToPhase (final Phase phase, final CompilationInfo currentInfo, final boolean cancellable) throws IOException JavaDoc {
749         Phase currentPhase = currentInfo.getPhase();
750         final boolean isMultiFiles = currentInfo.getSource().files.size()>1;
751         LowMemoryNotifier lm = null;
752         LMListener lmListener = null;
753         if (isMultiFiles) {
754             lm = LowMemoryNotifier.getDefault();
755             assert lm != null;
756             lmListener = new LMListener ();
757             lm.addLowMemoryListener (lmListener);
758         }
759         try {
760             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
761                 currentInfo.needsRestart = true;
762                 return currentPhase;
763             }
764             if (currentPhase.compareTo(Phase.PARSED)<0 && phase.compareTo(Phase.PARSED)>=0) {
765                 if (cancellable && currentRequest.isCanceled()) {
766                     //Keep the currentPhase unchanged, it may happen that an userActionTask
767
//runnig after the phace completion task may still use it.
768
return Phase.MODIFIED;
769                 }
770                 long start = System.currentTimeMillis();
771                 Language language = LanguageRegistry.getInstance().getLanguageByMimeType(currentInfo.getFileObject().getMIMEType());
772                 assert language != null;
773                 CompilationUnitTree unit = new CompilationUnitTree();
774                 Parser parser = language.getParser(); // Todo - call createParserTask here?
775
if (parser != null) {
776                     final String JavaDoc buffer = currentInfo.getText();
777                     
778                     final ParserResult[] resultHolder = new ParserResult[1];
779                     ParseListener listener = new ParseListener() {
780                         public void started(ParseEvent e) {
781                         }
782                         
783                         public void error(Error JavaDoc error) {
784                             currentInfo.addError(error);
785                         }
786                         
787                         public void exception(Exception JavaDoc exception) {
788                             Exceptions.printStackTrace(exception);
789                         }
790
791                         public void finished(ParseEvent e) {
792                             // TODO - check state
793
if (e.getKind() == ParseEvent.Kind.PARSE) {
794                                 resultHolder[0] = e.getResult();
795                             }
796                         }
797                         
798                     };
799                     List JavaDoc<ParserFile> sourceFiles = new ArrayList JavaDoc<ParserFile>(1);
800                     sourceFiles.add(new DefaultParserFile(currentInfo.getFileObject(), null, false));
801                     final FileObject bufferFo = currentInfo.getFileObject();
802                     SourceFileReader reader = new SourceFileReader() {
803                         public CharSequence JavaDoc read(ParserFile fileObject) {
804                             assert fileObject.getFileObject() == bufferFo;
805                             return buffer;
806                         }
807                         
808                         public int getCaretOffset(ParserFile file) {
809                             assert file.getFileObject() == bufferFo;
810                             int offset = CaretAwareSourceTaskFactory.getLastPosition(file.getFileObject());
811                             if (offset == 0) {
812                                 // CaretAwareSourceTaskFactory return 0 for unknown positions. These
813
// could also be actual positions, but for now, treat 0 as an unknown
814
// position since it's unlikely we have errors there.
815
return -1;
816                             } else {
817                                 return offset;
818                             }
819                         }
820                     };
821                     parser.parseFiles(sourceFiles, listener, reader);
822                     ParserResult result = resultHolder[0];
823                     
824                     assert result != null;
825                     currentInfo.setParser(parser);
826                     currentInfo.setParserResult(result);
827                     currentInfo.setPositionManager(parser.getPositionManager());
828                     currentInfo.setCompilationUnit(unit);
829                 }
830
831                 currentPhase = Phase.PARSED;
832                 long end = System.currentTimeMillis();
833                 FileObject file = currentInfo.getFileObject();
834                 //TimesCollector.getDefault().reportReference(file, "compilationUnit", "[M] Compilation Unit", unit); //NOI18N
835
logTime (file,currentPhase,(end-start));
836             }
837             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
838                 currentInfo.needsRestart = true;
839                 return currentPhase;
840             }
841             if (currentPhase == Phase.PARSED && phase.compareTo(Phase.ELEMENTS_RESOLVED)>=0) {
842                 if (cancellable && currentRequest.isCanceled()) {
843                     return Phase.MODIFIED;
844                 }
845                 long start = System.currentTimeMillis();
846                 // Noop right now - revisit when I add a parser which needs it (groovy?)
847
//currentInfo.getParserTask().enter();
848
currentPhase = Phase.ELEMENTS_RESOLVED;
849                 long end = System.currentTimeMillis();
850                 logTime(currentInfo.getFileObject(),currentPhase,(end-start));
851            }
852             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
853                 currentInfo.needsRestart = true;
854                 return currentPhase;
855             }
856             if (currentPhase == Phase.ELEMENTS_RESOLVED && phase.compareTo(Phase.RESOLVED)>=0) {
857                 if (cancellable && currentRequest.isCanceled()) {
858                     return Phase.MODIFIED;
859                 }
860                 long start = System.currentTimeMillis ();
861                 // Noop right now - revisit when I add a parser which needs it (groovy?)
862
//currentInfo.getParserTask().analyze();
863
currentPhase = Phase.RESOLVED;
864                 long end = System.currentTimeMillis ();
865                 logTime(currentInfo.getFileObject(),currentPhase,(end-start));
866             }
867             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
868                 currentInfo.needsRestart = true;
869                 return currentPhase;
870             }
871             if (currentPhase == Phase.RESOLVED && phase.compareTo(Phase.UP_TO_DATE)>=0) {
872                 currentPhase = Phase.UP_TO_DATE;
873             }
874         //} catch (Error abort) { // Abort in com.sun.tools is not here
875
// currentPhase = Phase.UP_TO_DATE;
876
// } catch (IOException ex) {
877
// dumpSource(currentInfo, ex);
878
// throw ex;
879
} catch (RuntimeException JavaDoc ex) {
880             dumpSource(currentInfo, ex);
881             throw ex;
882         } catch (java.lang.Error JavaDoc ex) {
883             dumpSource(currentInfo, ex);
884             throw ex;
885         }
886         finally {
887             if (isMultiFiles) {
888                 assert lm != null;
889                 assert lmListener != null;
890                 lm.removeLowMemoryListener (lmListener);
891             }
892             currentInfo.setPhase(currentPhase);
893         }
894         return currentPhase;
895     }
896
897     static void logTime (FileObject source, Phase phase, long time) {
898         assert source != null && phase != null;
899         String JavaDoc key = phase2Key.get(phase);
900         String JavaDoc message = phase2Message.get(phase);
901         assert key != null && message != null;
902         //TimesCollector.getDefault().reportTime (source,key,message,time);
903
}
904
905     private final RequestProcessor.Task resetTask = RequestProcessor.getDefault().create(new Runnable JavaDoc() {
906         public void run() {
907             resetStateImpl();
908         }
909     });
910
911     private void resetState(boolean invalidate, boolean updateIndex) {
912         boolean invalid;
913         synchronized (this) {
914             invalid = (this.flags & INVALID) != 0;
915             this.flags|=CHANGE_EXPECTED;
916             if (invalidate) {
917                 this.flags|=(INVALID|RESCHEDULE_FINISHED_TASKS);
918             }
919             if (updateIndex) {
920                 this.flags|=UPDATE_INDEX;
921             }
922         }
923         if (updateIndex && !invalid) {
924             //First change set the index as dirty
925
updateIndex ();
926         }
927         Request r = currentRequest.getTaskToCancel (this);
928         try {
929             if (r != null) {
930                 r.task.cancel();
931             }
932         }
933         finally {
934             currentRequest.cancelCompleted(r);
935         }
936         resetTask.schedule(REPARSE_DELAY);
937     }
938     
939
940     /**
941      * Not synchronized, only sets the atomic state and clears the listeners
942      *
943      */

944     private void resetStateImpl() {
945         synchronized (Source.class) {
946             boolean reschedule, updateIndex;
947             synchronized (this) {
948                 reschedule = (this.flags & RESCHEDULE_FINISHED_TASKS) != 0;
949                 updateIndex = (this.flags & UPDATE_INDEX) != 0;
950                 this.flags&=~(RESCHEDULE_FINISHED_TASKS|CHANGE_EXPECTED|UPDATE_INDEX);
951             }
952             if (updateIndex) {
953                 //Last change set the index as dirty
954
updateIndex ();
955             }
956             Collection JavaDoc<Request> cr;
957             if (reschedule) {
958                 if ((cr=Source.finishedRequests.remove(this)) != null && cr.size()>0) {
959                     Source.requests.addAll(cr);
960                 }
961             }
962             if ((cr=Source.waitingRequests.remove(this)) != null && cr.size()>0) {
963                 Source.requests.addAll(cr);
964             }
965         }
966     }
967
968     private void updateIndex () {
969         if (this.rootFo != null) {
970             try {
971                 ClassIndexImpl ciImpl = ClassIndexManager.getDefault().getUsagesQuery(this.rootFo.getURL());
972                 if (ciImpl != null) {
973                     ciImpl.setDirty(this);
974                 }
975             } catch (IOException JavaDoc ioe) {
976                 Exceptions.printStackTrace(ioe);
977             }
978         }
979     }
980
981     private void assignDocumentListener(FileObject fo) throws IOException JavaDoc {
982         DataObject od = DataObject.find(fo);
983         EditorCookie.Observable ec = (EditorCookie.Observable) od.getCookie(EditorCookie.Observable.class);
984         if (ec != null) {
985             this.listener = new DocListener (ec);
986         } else {
987             Logger.getLogger("global").log(Level.WARNING,String.format("File: %s has no EditorCookie.Observable", FileUtil.getFileDisplayName (fo))); //NOI18N
988
}
989     }
990
991     private static class Request {
992         private final CancellableTask<? extends CompilationInfo> task;
993         private final Source javaSource; //XXX: Maybe week, depends on the semantics
994
private final Phase phase;
995         private final Priority priority;
996         private final boolean reschedule;
997
998         public Request (final CancellableTask<? extends CompilationInfo> task, final Source javaSource,
999             final Phase phase, final Priority priority, final boolean reschedule) {
1000            assert task != null;
1001            this.task = task;
1002            this.javaSource = javaSource;
1003            this.phase = phase;
1004            this.priority = priority;
1005            this.reschedule = reschedule;
1006        }
1007
1008        public @Override JavaDoc String JavaDoc toString () {
1009            if (reschedule) {
1010                return String.format("Periodic request for phase: %s with priority: %s to perform: %s", phase.name(), priority, task.toString()); //NOI18N
1011
}
1012            else {
1013                return String.format("One time request for phase: %s with priority: %d to perform: %s", phase != null ? phase.name() : "<null>", priority, task.toString()); //NOI18N
1014
}
1015        }
1016
1017        public @Override JavaDoc int hashCode () {
1018            return this.priority.ordinal();
1019        }
1020
1021        public @Override JavaDoc boolean equals (Object JavaDoc other) {
1022            if (other instanceof Request) {
1023                Request otherRequest = (Request) other;
1024                return priority == otherRequest.priority
1025                    && reschedule == otherRequest.reschedule
1026                    && phase.equals (otherRequest.phase)
1027                    && task.equals(otherRequest.task);
1028            }
1029            else {
1030                return false;
1031            }
1032        }
1033    }
1034
1035    private static class RequestComparator implements Comparator JavaDoc<Request> {
1036        public int compare (Request r1, Request r2) {
1037            assert r1 != null && r2 != null;
1038            return r1.priority.compareTo (r2.priority);
1039        }
1040    }
1041
1042    private static class CompilationJob implements Runnable JavaDoc {
1043
1044        @SuppressWarnings JavaDoc ("unchecked") //NOI18N
1045
public void run () {
1046            try {
1047                while (true) {
1048                    try {
1049                        synchronized (Source.class) {
1050                            //Clean up toRemove tasks
1051
if (!toRemove.isEmpty()) {
1052                                for (Iterator JavaDoc<Collection JavaDoc<Request>> it = finishedRequests.values().iterator(); it.hasNext();) {
1053                                    Collection JavaDoc<Request> cr = it.next ();
1054                                    for (Iterator JavaDoc<Request> it2 = cr.iterator(); it2.hasNext();) {
1055                                        Request fr = it2.next();
1056                                        if (toRemove.remove(fr.task)) {
1057                                            it2.remove();
1058                                        }
1059                                    }
1060                                    if (cr.size()==0) {
1061                                        it.remove();
1062                                    }
1063                                }
1064                            }
1065                        }
1066                        Request r = Source.requests.poll(2,TimeUnit.SECONDS);
1067                        if (r != null) {
1068                            currentRequest.setCurrentTask(r);
1069                            try {
1070                                Source js = r.javaSource;
1071                                if (js == null) {
1072                                    assert r.phase == null;
1073                                    assert r.reschedule == false;
1074                                    javacLock.lock ();
1075                                    try {
1076                                        r.task.run (null);
1077                                    } catch (RuntimeException JavaDoc re) {
1078                                        Exceptions.printStackTrace(re);
1079                                    }
1080                                    finally {
1081                                        javacLock.unlock();
1082                                    }
1083                                }
1084                                else {
1085                                    assert js.files.size() <= 1;
1086                                    boolean jsInvalid;
1087                                    CompilationInfo ci;
1088                                    synchronized (Source.class) {
1089                                        //jl:what does this comment mean?
1090
//Not only the finishedRequests for the current request.javaSource should be cleaned,
1091
//it will cause a starvation
1092
if (toRemove.remove(r.task)) {
1093                                            continue;
1094                                        }
1095                                        synchronized (js) {
1096                                            boolean changeExpected = (js.flags & CHANGE_EXPECTED) != 0;
1097                                            if (changeExpected) {
1098                                                //Skip the task, another invalidation is comming
1099
Collection JavaDoc<Request> rc = Source.waitingRequests.get (r.javaSource);
1100                                                if (rc == null) {
1101                                                    rc = new LinkedList JavaDoc<Request> ();
1102                                                    Source.waitingRequests.put (r.javaSource, rc);
1103                                                }
1104                                                rc.add(r);
1105                                                continue;
1106                                            }
1107                                            jsInvalid = (js.flags & INVALID)!=0;
1108                                            ci = js.currentInfo;
1109                                        }
1110                                    }
1111                                    try {
1112                                        //createCurrentInfo has to be out of synchronized block, it aquires an editor lock
1113
if (jsInvalid) {
1114                                            ci = createCurrentInfo (js,js.files.isEmpty() ? null : js.files.iterator().next(),null);
1115                                            synchronized (js) {
1116                                                if ((js.flags & INVALID) != 0) {
1117                                                    js.currentInfo = ci;
1118                                                    js.flags &= ~INVALID;
1119                                                }
1120                                                else {
1121                                                    ci = js.currentInfo;
1122                                                }
1123                                            }
1124                                        }
1125                                        assert ci != null;
1126                                        javacLock.lock();
1127                                        try {
1128                                            final Phase phase = Source.moveToPhase (r.phase, ci, true);
1129                                            boolean shouldCall = phase.compareTo(r.phase)>=0;
1130                                            if (shouldCall) {
1131                                                synchronized (js) {
1132                                                    shouldCall &= (js.flags & INVALID)==0;
1133                                                }
1134                                                if (shouldCall) {
1135                                                    //The state (or greater) was reached and document was not modified during moveToPhase
1136
try {
1137                                                        final long startTime = System.currentTimeMillis();
1138                                                        ((CancellableTask<CompilationInfo>)r.task).run (ci); //XXX: How to do it in save way?
1139
final long endTime = System.currentTimeMillis();
1140                                                        if (reportSlowTasks) {
1141                                                            if ((endTime - startTime) > SLOW_TASK_LIMIT) {
1142                                                                Logger.getLogger("global").log(Level.INFO,String.format("Source executed a slow task: %s in %d ms.", //NOI18N
1143
r.task.getClass().toString(), (endTime-startTime)));
1144                                                            }
1145                                                            final long cancelTime = currentRequest.getCancelTime();
1146                                                            if (cancelTime >= startTime && (endTime - cancelTime) > SLOW_CANCEL_LIMIT) {
1147                                                                Logger.getLogger("global").log(Level.INFO,String.format("Task: %s ignored cancel for %d ms.", //NOI18N
1148
r.task.getClass().toString(), (endTime-cancelTime)));
1149                                                            }
1150                                                        }
1151                                                    } catch (RuntimeException JavaDoc re) {
1152                                                        Exceptions.printStackTrace (re);
1153                                                    }
1154                                                }
1155                                            }
1156                                        } finally {
1157                                            javacLock.unlock();
1158                                        }
1159
1160                                        if (r.reschedule) {
1161                                            synchronized (Source.class) {
1162                                                boolean canceled = currentRequest.setCurrentTask(null);
1163                                                synchronized (js) {
1164                                                    if ((js.flags & INVALID)!=0 || canceled) {
1165                                                        //The Source was changed or canceled rechedule it now
1166
Source.requests.add(r);
1167                                                    }
1168                                                    else {
1169                                                        //Up to date Source add it to the finishedRequests
1170
Collection JavaDoc<Request> rc = Source.finishedRequests.get (r.javaSource);
1171                                                        if (rc == null) {
1172                                                            rc = new LinkedList JavaDoc<Request> ();
1173                                                            Source.finishedRequests.put (r.javaSource, rc);
1174                                                        }
1175                                                        rc.add(r);
1176                                                    }
1177                                                }
1178                                            }
1179                                        }
1180                                    } catch (final IOException JavaDoc invalidFile) {
1181                                        //Ideally the requests should be removed by JavaSourceTaskFactory and task should be put to finishedRequests,
1182
//but the reality is different, the task cannot be put to finished request because of possible memory leak
1183
}
1184                                }
1185                            } finally {
1186                                currentRequest.setCurrentTask(null);
1187                            }
1188                        }
1189                    } catch (Throwable JavaDoc e) {
1190                        if (e instanceof InterruptedException JavaDoc) {
1191                            throw (InterruptedException JavaDoc)e;
1192                        }
1193                        else if (e instanceof ThreadDeath JavaDoc) {
1194                            throw (ThreadDeath JavaDoc)e;
1195                        }
1196                        else {
1197                            Exceptions.printStackTrace(e);
1198                        }
1199                    }
1200                }
1201            } catch (InterruptedException JavaDoc ie) {
1202                ie.printStackTrace();
1203                // stop the service.
1204
}
1205        }
1206    }
1207
1208    private class DocListener implements DocumentListener JavaDoc, PropertyChangeListener JavaDoc, ChangeListener JavaDoc {
1209
1210        private EditorCookie.Observable ec;
1211        private DocumentListener JavaDoc docListener;
1212
1213        public DocListener (EditorCookie.Observable ec) {
1214            assert ec != null;
1215            this.ec = ec;
1216            this.ec.addPropertyChangeListener((PropertyChangeListener JavaDoc)WeakListeners.propertyChange(this, this.ec));
1217            Document JavaDoc doc = ec.getDocument();
1218            if (doc != null) {
1219                doc.addDocumentListener(docListener = WeakListeners.create(DocumentListener JavaDoc.class, this, doc));
1220            }
1221        }
1222
1223        public void insertUpdate(DocumentEvent JavaDoc e) {
1224            //Has to reset cache asynchronously
1225
//the callback cannot be in synchronized section
1226
//since NbDocument.runAtomic fires under lock
1227
Source.this.resetState(true, true);
1228        }
1229
1230        public void removeUpdate(DocumentEvent JavaDoc e) {
1231            //Has to reset cache asynchronously
1232
//the callback cannot be in synchronized section
1233
//since NbDocument.runAtomic fires under lock
1234
Source.this.resetState(true, true);
1235        }
1236
1237        public void changedUpdate(DocumentEvent JavaDoc e) {
1238        }
1239
1240        public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1241            if (EditorCookie.Observable.PROP_DOCUMENT.equals(evt.getPropertyName())) {
1242                Object JavaDoc old = evt.getOldValue();
1243                if (old instanceof Document JavaDoc && docListener != null) {
1244                    ((Document JavaDoc) old).removeDocumentListener(docListener);
1245                    docListener = null;
1246                }
1247                Document JavaDoc doc = ec.getDocument();
1248                if (doc != null) {
1249                    doc.addDocumentListener(docListener = WeakListeners.create(DocumentListener JavaDoc.class, this, doc));
1250                }
1251            }
1252        }
1253
1254        public void stateChanged(ChangeEvent JavaDoc e) {
1255            Source.this.resetState(true, false);
1256        }
1257
1258    }
1259
1260    private static class EditorRegistryListener implements ChangeListener JavaDoc, CaretListener JavaDoc {
1261
1262        private JTextComponent JavaDoc lastEditor;
1263
1264        public EditorRegistryListener () {
1265            Registry.addChangeListener(this);
1266        }
1267
1268        public void stateChanged(ChangeEvent JavaDoc event) {
1269            final JTextComponent JavaDoc editor = Registry.getMostActiveComponent();
1270            if (lastEditor != editor) {
1271                if (lastEditor != null) {
1272                    lastEditor.removeCaretListener(this);
1273                }
1274                lastEditor = editor;
1275                if (lastEditor != null) {
1276                    lastEditor.addCaretListener(this);
1277                }
1278            }
1279        }
1280
1281        public void caretUpdate(CaretEvent JavaDoc event) {
1282            if (lastEditor != null) {
1283                Document JavaDoc doc = lastEditor.getDocument();
1284                if (doc != null) {
1285                    Source js = forDocument(doc);
1286                    if (js != null) {
1287                        js.resetState(false, false);
1288                    }
1289                }
1290            }
1291        }
1292    }
1293
1294    private class FileChangeListenerImpl extends FileChangeAdapter {
1295
1296        public @Override JavaDoc void fileChanged(final FileEvent fe) {
1297            Source.this.resetState(true, false);
1298        }
1299
1300        public @Override JavaDoc void fileRenamed(FileRenameEvent fe) {
1301            Source.this.resetState(true, false);
1302        }
1303    }
1304
1305    private final class DataObjectListener implements PropertyChangeListener JavaDoc {
1306
1307        private DataObject dobj;
1308        private final FileObject fobj;
1309        private PropertyChangeListener JavaDoc wlistener;
1310
1311        public DataObjectListener(FileObject fo) throws DataObjectNotFoundException {
1312            this.fobj = fo;
1313            this.dobj = DataObject.find(fo);
1314            wlistener = WeakListeners.propertyChange(this, dobj);
1315            this.dobj.addPropertyChangeListener(wlistener);
1316        }
1317
1318        public void propertyChange(PropertyChangeEvent JavaDoc pce) {
1319            DataObject invalidDO = (DataObject) pce.getSource();
1320            if (invalidDO != dobj)
1321                return;
1322            if (DataObject.PROP_VALID.equals(pce.getPropertyName())) {
1323                handleInvalidDataObject(invalidDO);
1324            } else if (pce.getPropertyName() == null && !dobj.isValid()) {
1325                handleInvalidDataObject(invalidDO);
1326            }
1327        }
1328
1329        private void handleInvalidDataObject(DataObject invalidDO) {
1330            invalidDO.removePropertyChangeListener(wlistener);
1331            if (fobj.isValid()) {
1332                // file object still exists try to find new data object
1333
try {
1334                    dobj = DataObject.find(fobj);
1335                    dobj.addPropertyChangeListener(wlistener);
1336                    assignDocumentListener(fobj);
1337                    resetState(true, true);
1338                } catch (IOException JavaDoc ex) {
1339                    // should not occur
1340
Logger.getLogger(Source.class.getName()).log(Level.SEVERE,
1341                                                                     ex.getMessage(),
1342                                                                     ex);
1343                }
1344            }
1345        }
1346
1347    }
1348
1349    private static CompilationInfo createCurrentInfo (final Source js, final FileObject fo, final ParserTaskImpl javac) throws IOException JavaDoc {
1350        if (js.sourceLevel == null && fo != null)
1351            js.sourceLevel = SourceLevelQuery.getSourceLevel(fo);
1352        if (js.sourceLevel == null)
1353            js.sourceLevel = JavaPlatformManager.getDefault().getDefaultPlatform().getSpecification().getVersion().toString();
1354        CompilationInfo info = new CompilationInfo (js, fo, javac);
1355        info.setLanguage(LanguageRegistry.getInstance().getLanguageByMimeType(fo.getMIMEType()));
1356        //TimesCollector.getDefault().reportReference(fo, CompilationInfo.class.toString(), "[M] CompilationInfo", info); //NOI18N
1357
return info;
1358    }
1359
1360    private static void handleAddRequest (final Request nr) {
1361        assert nr != null;
1362        requests.add (nr);
1363        Source.Request request = currentRequest.getTaskToCancel(nr.priority);
1364        try {
1365            if (request != null) {
1366                request.task.cancel();
1367            }
1368        } finally {
1369            currentRequest.cancelCompleted(request);
1370        }
1371    }
1372
1373    private static class SingleThreadFactory implements ThreadFactory JavaDoc {
1374
1375        private Thread JavaDoc t;
1376
1377        public Thread JavaDoc newThread(Runnable JavaDoc r) {
1378            assert this.t == null;
1379            this.t = new Thread JavaDoc (r,"GSF Source Worker Thread"); //NOI18N
1380
return this.t;
1381        }
1382
1383        public boolean isDispatchThread (Thread JavaDoc t) {
1384            assert t != null;
1385            return this.t == t;
1386        }
1387    }
1388
1389    private static class JavaSourceAccessorImpl extends SourceAccessor {
1390
1391        protected @Override JavaDoc void runSpecialTaskImpl (CancellableTask<CompilationInfo> task, Priority priority) {
1392            handleAddRequest(new Request (task, null, null, priority, false));
1393        }
1394
1395        @Override JavaDoc
1396        public ParserTaskImpl createParserTask(Language language, ClasspathInfo cpInfo, /*DiagnosticListener<? super SourceFileObject> diagnosticListener,*/ String JavaDoc sourceLevel) {
1397            if (sourceLevel == null)
1398                sourceLevel = JavaPlatformManager.getDefault().getDefaultPlatform().getSpecification().getVersion().toString();
1399// return Source.createParserTask(cpInfo, diagnosticListener, sourceLevel, true);
1400
// throw new RuntimeException("Not yet implemented - I need the CompilationInfo here so I can pass in the language etc.");
1401
boolean backgroundCompilation = true; // Is this called from anywhere else?
1402
return Source.createParserTask(language, null, cpInfo, sourceLevel, backgroundCompilation);
1403        }
1404
1405        @Override JavaDoc
1406        public ParserTaskImpl/*JavacTaskImpl*/ getParserTask (final CompilationInfo compilationInfo) {
1407            assert compilationInfo != null;
1408            return compilationInfo.getParserTask();
1409        }
1410        
1411        @Override JavaDoc
1412        public CompilationInfo getCurrentCompilationInfo (final Source js, final Phase phase) throws IOException JavaDoc {
1413            assert js != null;
1414            assert isDispatchThread();
1415            CompilationInfo info = null;
1416            synchronized (js) {
1417                if ((js.flags & INVALID)==0) {
1418                    info = js.currentInfo;
1419                }
1420            }
1421            if (info == null) {
1422                return null;
1423            }
1424            Phase currentPhase = moveToPhase(phase, info, true);
1425            return currentPhase.compareTo(phase)<0 ? null : info;
1426        }
1427
1428        @Override JavaDoc
1429        public void revalidate(Source js) {
1430            js.revalidate();
1431        }
1432
1433        @Override JavaDoc
1434        public boolean isDispatchThread () {
1435            return factory.isDispatchThread(Thread.currentThread());
1436         }
1437    
1438    }
1439
1440    private static class CurrentRequestReference {
1441
1442
1443        private Source.Request reference;
1444        private Source.Request canceledReference;
1445        private long cancelTime;
1446        private boolean canceled;
1447
1448        public CurrentRequestReference () {
1449        }
1450
1451        public boolean setCurrentTask (Source.Request reference) throws InterruptedException JavaDoc {
1452            boolean result = false;
1453            synchronized (this) {
1454                while (this.canceledReference!=null) {
1455                    this.wait();
1456                }
1457                result = this.canceled;
1458                this.canceled = false;
1459                this.cancelTime = 0;
1460                this.reference = reference;
1461            }
1462            return result;
1463        }
1464
1465        public Source.Request getTaskToCancel (final Priority priority) {
1466            Source.Request request = null;
1467            if (!factory.isDispatchThread(Thread.currentThread())) {
1468                synchronized (this) {
1469                    if (this.reference != null && priority.compareTo(this.reference.priority) < 0) {
1470                        assert this.canceledReference == null;
1471                        request = this.reference;
1472                        this.canceledReference = request;
1473                        this.reference = null;
1474                        this.canceled = true;
1475                        if (reportSlowTasks) {
1476                            cancelTime = System.currentTimeMillis();
1477                        }
1478                    }
1479                }
1480            }
1481            return request;
1482        }
1483
1484        public Source.Request getTaskToCancel (final Source js) {
1485            Source.Request request = null;
1486            if (!factory.isDispatchThread(Thread.currentThread())) {
1487                synchronized (this) {
1488                    if (this.reference != null && js.equals(this.reference.javaSource)) {
1489                        assert this.canceledReference == null;
1490                        request = this.reference;
1491                        this.canceledReference = request;
1492                        this.reference = null;
1493                        this.canceled = true;
1494                        if (reportSlowTasks) {
1495                            cancelTime = System.currentTimeMillis();
1496                        }
1497                    }
1498                }
1499            }
1500            return request;
1501        }
1502
1503        public Source.Request getTaskToCancel (final CancellableTask task) {
1504            Source.Request request = null;
1505            if (!factory.isDispatchThread(Thread.currentThread())) {
1506                synchronized (this) {
1507                    if (this.reference != null && task == this.reference.task) {
1508                        assert this.canceledReference == null;
1509                        request = this.reference;
1510                        this.canceledReference = request;
1511                        this.reference = null;
1512                        this.canceled = true;
1513                    }
1514                }
1515            }
1516            return request;
1517        }
1518
1519        public Source.Request getTaskToCancel () {
1520            Source.Request request = null;
1521            if (!factory.isDispatchThread(Thread.currentThread())) {
1522                synchronized (this) {
1523                     request = this.reference;
1524                    if (request != null) {
1525                        assert this.canceledReference == null;
1526                        this.canceledReference = request;
1527                        this.reference = null;
1528                        this.canceled = true;
1529                        if (reportSlowTasks) {
1530                            cancelTime = System.currentTimeMillis();
1531                        }
1532                    }
1533                }
1534            }
1535            return request;
1536        }
1537
1538        public synchronized boolean isCanceled () {
1539            return this.canceled;
1540        }
1541
1542        public synchronized long getCancelTime () {
1543            return this.cancelTime;
1544        }
1545
1546        public void cancelCompleted (final Source.Request request) {
1547            if (request != null) {
1548                synchronized (this) {
1549                    assert request == this.canceledReference;
1550                    this.canceledReference = null;
1551                    this.notify();
1552                }
1553            }
1554        }
1555    }
1556
1557    private static class LMListener implements LowMemoryListener {
1558        private AtomicBoolean JavaDoc lowMemory = new AtomicBoolean JavaDoc (false);
1559
1560        public void lowMemory(LowMemoryEvent event) {
1561            lowMemory.set(true);
1562        }
1563    }
1564
1565    /**
1566     *Ugly and slow, called only when -ea
1567     *
1568     */

1569    private static boolean holdsDocumentWriteLock (Iterable JavaDoc<FileObject> files) {
1570        final Class JavaDoc<AbstractDocument JavaDoc> docClass = AbstractDocument JavaDoc.class;
1571        try {
1572            final Method JavaDoc method = docClass.getDeclaredMethod("getCurrentWriter"); //NOI18N
1573
method.setAccessible(true);
1574            final Thread JavaDoc currentThread = Thread.currentThread();
1575            for (FileObject fo : files) {
1576                try {
1577                final DataObject dobj = DataObject.find(fo);
1578                final EditorCookie ec = (EditorCookie) dobj.getCookie(EditorCookie.class);
1579                if (ec != null) {
1580                    final StyledDocument JavaDoc doc = ec.getDocument();
1581                    if (doc instanceof AbstractDocument JavaDoc) {
1582                        Object JavaDoc result = method.invoke(doc);
1583                        if (result == currentThread) {
1584                            return true;
1585                        }
1586                    }
1587                }
1588                } catch (Exception JavaDoc e) {
1589                    Exceptions.printStackTrace(e);
1590                }
1591            }
1592        } catch (NoSuchMethodException JavaDoc e) {
1593            Exceptions.printStackTrace(e);
1594        }
1595        return false;
1596    }
1597
1598    private static final int MAX_DUMPS = 255;
1599
1600    /**
1601     * Dumps the source code to the file. Used for parser debugging. Only a limited number
1602     * of dump files is used. If the last file exists, this method doesn't dump anything.
1603     *
1604     * @param info CompilationInfo for which the error occurred.
1605     * @param exc exception to write to the end of dump file
1606     */

1607    private static void dumpSource(CompilationInfo info, Throwable JavaDoc exc) {
1608        String JavaDoc dumpDir = System.getProperty("netbeans.user") + "/var/log/"; //NOI18N
1609
String JavaDoc src = info.getText();
1610        FileObject file = info.getFileObject();
1611        String JavaDoc fileName = FileUtil.getFileDisplayName(info.getFileObject());
1612        String JavaDoc origName = file.getName();
1613        File JavaDoc f = new File JavaDoc(dumpDir + origName + ".dump"); // NOI18N
1614
boolean dumpSucceeded = false;
1615        int i = 1;
1616        while (i < MAX_DUMPS) {
1617            if (!f.exists())
1618                break;
1619            f = new File JavaDoc(dumpDir + origName + '_' + i + ".dump"); // NOI18N
1620
i++;
1621        }
1622        if (!f.exists()) {
1623            try {
1624                OutputStream JavaDoc os = new FileOutputStream JavaDoc(f);
1625                PrintWriter JavaDoc writer = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(os, "UTF-8")); // NOI18N
1626
try {
1627                    writer.println(src);
1628                    writer.println("----- Classpath: ---------------------------------------------"); // NOI18N
1629

1630                    final ClassPath bootPath = info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.BOOT);
1631                    final ClassPath classPath = info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.COMPILE);
1632                    final ClassPath sourcePath = info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.SOURCE);
1633
1634                    writer.println("bootPath: " + (bootPath != null ? bootPath.toString() : "null"));
1635                    writer.println("classPath: " + (classPath != null ? classPath.toString() : "null"));
1636                    writer.println("sourcePath: " + (sourcePath != null ? sourcePath.toString() : "null"));
1637
1638                    writer.println("----- Original exception ---------------------------------------------"); // NOI18N
1639
exc.printStackTrace(writer);
1640                } finally {
1641                    writer.close();
1642                    dumpSucceeded = true;
1643                }
1644            } catch (IOException JavaDoc ioe) {
1645                Logger.getLogger("global").log(Level.INFO, "Error when writing parser dump file!", ioe); // NOI18N
1646
}
1647        }
1648        if (dumpSucceeded) {
1649            Throwable JavaDoc t = Exceptions.attachMessage(exc, "An error occurred during parsing of \'" + fileName + "\'. Please report a bug against scripting/ruby and attach dump file '" // NOI18N
1650
+ f.getAbsolutePath() + "'."); // NOI18N
1651
Exceptions.printStackTrace(t);
1652        } else {
1653            Logger.getLogger("global").log(Level.WARNING,
1654                    "Dump could not be written. Either dump file could not " + // NOI18N
1655
"be created or all dump files were already used. Please " + // NOI18N
1656
"check that you have write permission to '" + dumpDir + "' and " + // NOI18N
1657
"clean all *.dump files in that directory."); // NOI18N
1658
}
1659    }
1660}
1661
Popular Tags