KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > api > java > source > JavaSource


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.java.source;
21
22 import com.sun.source.tree.CompilationUnitTree;
23 import com.sun.tools.javac.api.JavacTaskImpl;
24 import com.sun.tools.javac.code.Source;
25 import com.sun.tools.javac.code.Source;
26 import com.sun.tools.javac.comp.Enter;
27 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
28 import com.sun.tools.javac.util.Abort;
29 import com.sun.tools.javac.util.Context;
30 import com.sun.tools.javac.util.CouplingAbort;
31 import com.sun.tools.javac.util.Log;
32 import com.sun.tools.javadoc.JavadocEnter;
33 import com.sun.tools.javadoc.JavadocMemberEnter;
34 import com.sun.tools.javadoc.Messager;
35 import java.beans.PropertyChangeEvent JavaDoc;
36 import java.beans.PropertyChangeListener JavaDoc;
37 import java.io.File JavaDoc;
38 import java.io.FileOutputStream JavaDoc;
39 import java.io.IOException JavaDoc;
40 import java.io.OutputStream JavaDoc;
41 import java.io.OutputStreamWriter JavaDoc;
42 import java.io.PrintWriter JavaDoc;
43 import java.lang.ref.Reference JavaDoc;
44 import java.lang.ref.WeakReference JavaDoc;
45 import java.lang.reflect.Method JavaDoc;
46 import java.util.ArrayList JavaDoc;
47 import java.util.Arrays JavaDoc;
48 import java.util.Collection JavaDoc;
49 import java.util.Collections JavaDoc;
50 import java.util.Comparator JavaDoc;
51 import java.util.HashMap JavaDoc;
52 import java.util.Iterator JavaDoc;
53 import java.util.LinkedList JavaDoc;
54 import java.util.List JavaDoc;
55 import java.util.Map JavaDoc;
56 import java.util.WeakHashMap JavaDoc;
57 import java.util.concurrent.Executors JavaDoc;
58 import java.util.concurrent.PriorityBlockingQueue JavaDoc;
59 import java.util.concurrent.ThreadFactory JavaDoc;
60 import java.util.concurrent.TimeUnit JavaDoc;
61 import java.util.concurrent.atomic.AtomicBoolean JavaDoc;
62 import java.util.concurrent.locks.ReentrantLock JavaDoc;
63 import java.util.logging.Level JavaDoc;
64 import java.util.logging.Logger JavaDoc;
65 import javax.swing.event.CaretEvent JavaDoc;
66 import javax.swing.event.CaretListener JavaDoc;
67 import javax.swing.event.ChangeEvent JavaDoc;
68 import javax.swing.event.ChangeListener JavaDoc;
69 import javax.swing.event.ChangeListener JavaDoc;
70 import javax.swing.event.DocumentEvent JavaDoc;
71 import javax.swing.event.DocumentListener JavaDoc;
72 import javax.swing.text.AbstractDocument JavaDoc;
73 import javax.swing.text.Document JavaDoc;
74 import javax.swing.text.JTextComponent JavaDoc;
75 import javax.swing.text.StyledDocument JavaDoc;
76 import javax.tools.DiagnosticListener;
77 import javax.tools.JavaCompiler;
78 import javax.tools.JavaFileObject;
79 import javax.tools.ToolProvider;
80 import org.netbeans.api.java.classpath.ClassPath;
81 import org.netbeans.api.java.platform.JavaPlatformManager;
82 import org.netbeans.api.java.queries.SourceLevelQuery;
83 import org.netbeans.api.java.source.ClasspathInfo.PathKind;
84 import org.netbeans.api.java.source.ModificationResult.Difference;
85 import org.netbeans.api.timers.TimesCollector;
86 import org.netbeans.editor.Registry;
87 import org.netbeans.api.java.source.query.QueryEnvironment;
88 import org.netbeans.modules.java.source.JavaFileFilterQuery;
89 import org.netbeans.modules.java.source.builder.ASTService;
90 import org.netbeans.modules.java.source.builder.Scanner;
91 import org.netbeans.modules.java.source.JavaSourceAccessor;
92 import org.netbeans.modules.java.source.JavadocEnv;
93 import org.netbeans.modules.java.source.engine.ReattributionException;
94 import org.netbeans.modules.java.source.engine.RootTree;
95 import org.netbeans.modules.java.source.parsing.FileObjects;
96 import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation;
97 import org.netbeans.modules.java.source.builder.UndoListService;
98 import org.netbeans.modules.java.source.usages.ClassIndexImpl;
99 import org.netbeans.modules.java.source.usages.ClassIndexManager;
100 import org.netbeans.modules.java.source.usages.RepositoryUpdater;
101 import org.netbeans.modules.java.source.util.LowMemoryEvent;
102 import org.netbeans.modules.java.source.util.LowMemoryListener;
103 import org.netbeans.modules.java.source.util.LowMemoryNotifier;
104 import org.netbeans.modules.java.source.usages.SymbolClassReader;
105 import org.openide.cookies.EditorCookie;
106 import org.openide.filesystems.FileChangeAdapter;
107 import org.openide.filesystems.FileChangeListener;
108 import org.openide.filesystems.FileEvent;
109 import org.openide.filesystems.FileObject;
110 import org.openide.filesystems.FileRenameEvent;
111 import org.openide.filesystems.FileUtil;
112 import org.openide.filesystems.FileUtil;
113 import org.openide.loaders.DataObject;
114 import org.openide.loaders.DataObjectNotFoundException;
115 import org.openide.modules.SpecificationVersion;
116 import org.openide.util.Exceptions;
117 import org.openide.util.NbBundle;
118 import org.openide.util.RequestProcessor;
119 import org.openide.util.WeakListeners;
120
121 /** Class representing Java source file opened in the editor.
122  *
123  * @author Petr Hrebejk, Tomas Zezula
124  */

125 public final class JavaSource {
126     
127     
128     public static enum Phase {
129         MODIFIED,
130     
131         PARSED,
132         
133         ELEMENTS_RESOLVED,
134     
135         RESOLVED,
136     
137         UP_TO_DATE;
138     
139     };
140     
141     public static enum Priority {
142         MAX,
143         HIGH,
144         ABOVE_NORMAL,
145         NORMAL,
146         BELOW_NORMAL,
147         LOW,
148         MIN
149     };
150     
151     
152     
153     /**
154      * This specialization of {@link IOException} signals that a {@link JavaSource#runUserActionTask}
155      * or {@link JavaSource#runModificationTask} failed due to lack of memory. The {@link InsufficientMemoryException#getFile}
156      * method returns a file which cannot be processed.
157      */

158     public static final class InsufficientMemoryException extends IOException JavaDoc {
159         
160         private FileObject fo;
161         
162         private InsufficientMemoryException (final String JavaDoc message, final FileObject fo) {
163             super (message);
164             this.fo = fo;
165         }
166         
167         private InsufficientMemoryException (FileObject fo) {
168             this (NbBundle.getMessage(JavaSource.class, "MSG_UnsufficientMemoryException", FileUtil.getFileDisplayName (fo)),fo);
169         }
170         
171         
172         /**
173          * Returns file which cannot be processed due to lack of memory.
174          * @return {@link FileObject}
175          */

176         public FileObject getFile () {
177             return this.fo;
178         }
179     }
180     
181     /**Constants for JavaSource.flags*/
182     private static final int INVALID = 1;
183     private static final int CHANGE_EXPECTED = INVALID<<1;
184     private static final int RESCHEDULE_FINISHED_TASKS = CHANGE_EXPECTED<<1;
185     private static final int UPDATE_INDEX = RESCHEDULE_FINISHED_TASKS<<1;
186     
187     /**Slow task reporting*/
188     private static final boolean reportSlowTasks = Boolean.getBoolean("org.netbeans.api.java.source.JavaSource.reportSlowTasks"); //NOI18N
189
/**Limit for task to be marked as a slow one, in ms*/
190     private static final int SLOW_TASK_LIMIT = 250;
191     private static final int SLOW_CANCEL_LIMIT = 50;
192     
193     /**Not final for tests.*/
194     static int REPARSE_DELAY = 500;
195     
196     /**Used by unit tests*/
197     static JavaFileObjectProvider jfoProvider = new DefaultJavaFileObjectProvider ();
198     
199     /**
200      * Helper maps mapping the {@link Phase} to key and message for
201      * the {@link TimesCollector}
202      */

203     private static Map JavaDoc<Phase, String JavaDoc> phase2Key = new HashMap JavaDoc<Phase,String JavaDoc> ();
204     private static Map JavaDoc<Phase, String JavaDoc> phase2Message = new HashMap JavaDoc<Phase,String JavaDoc> ();
205     
206     /**
207      * Init the maps
208      */

209     static {
210         JavaSourceAccessor.INSTANCE = new JavaSourceAccessorImpl ();
211         phase2Key.put(Phase.PARSED,"parsed"); //NOI18N
212
phase2Message.put (Phase.PARSED,"Parsed"); //NOI18N
213

214         phase2Key.put(Phase.ELEMENTS_RESOLVED,"sig-attributed"); //NOI18N
215
phase2Message.put (Phase.ELEMENTS_RESOLVED,"Signatures Attributed"); //NOI18N
216

217         phase2Key.put(Phase.RESOLVED, "attributed"); //NOI18N
218
phase2Message.put (Phase.RESOLVED, "Attributed"); //NOI18N
219

220     }
221                             
222     private final static PriorityBlockingQueue JavaDoc<Request> requests = new PriorityBlockingQueue JavaDoc<Request> (10, new RequestComparator());
223     private final static Map JavaDoc<JavaSource,Collection JavaDoc<Request>> finishedRequests = new WeakHashMap JavaDoc<JavaSource,Collection JavaDoc<Request>>();
224     private final static Map JavaDoc<JavaSource,Collection JavaDoc<Request>> waitingRequests = new WeakHashMap JavaDoc<JavaSource,Collection JavaDoc<Request>>();
225     private final static Collection JavaDoc<CancellableTask> toRemove = new LinkedList JavaDoc<CancellableTask> ();
226     private final static SingleThreadFactory factory = new SingleThreadFactory ();
227     private final static CurrentRequestReference currentRequest = new CurrentRequestReference ();
228     private final static EditorRegistryListener editorRegistryListener = new EditorRegistryListener ();
229     //Only single thread can operate on the single javac
230
private final static ReentrantLock JavaDoc javacLock = new ReentrantLock JavaDoc (true);
231     
232     private final Collection JavaDoc<FileObject> files;
233     private final FileObject rootFo;
234     private final FileChangeListener fileChangeListener;
235     private DocListener listener;
236     private DataObjectListener dataObjectListener;
237     
238     private final ClasspathInfo classpathInfo;
239     private CompilationInfo currentInfo;
240     private java.util.Stack JavaDoc<CompilationInfo> infoStack = new java.util.Stack JavaDoc<CompilationInfo> ();
241             
242     private int flags = 0;
243     
244     //Preprocessor support
245
private FilterListener filterListener;
246     
247         
248     static {
249         Executors.newSingleThreadExecutor(factory).submit (new CompilationJob());
250     }
251     
252     
253     /**
254      * Returns a {@link JavaSource} instance representing given {@link org.openide.filesystems.FileObject}s
255      * and classpath represented by given {@link ClasspathInfo}.
256      * @param cpInfo the classpaths to be used.
257      * @param files for which the {@link JavaSource} should be created
258      * @return a new {@link JavaSource}
259      * @throws {@link IllegalArgumentException} if fileObject or cpInfo is null
260      */

261     public static JavaSource create(final ClasspathInfo cpInfo, final Collection JavaDoc<? extends FileObject> files) throws IllegalArgumentException JavaDoc {
262         if (files == null || cpInfo == null) {
263             throw new IllegalArgumentException JavaDoc ();
264         }
265         try {
266             return new JavaSource(cpInfo, files);
267         } catch (DataObjectNotFoundException donf) {
268             Logger.getLogger("global").warning("Ignoring non existent file: " + FileUtil.getFileDisplayName(donf.getFileObject())); //NOI18N
269
} catch (IOException JavaDoc ex) {
270             Exceptions.printStackTrace(ex);
271         }
272         return null;
273     }
274     
275     
276     /**
277      * Returns a {@link JavaSource} instance representing given {@link org.openide.filesystems.FileObject}s
278      * and classpath represented by given {@link ClasspathInfo}.
279      * @param cpInfo the classpaths to be used.
280      * @param files for which the {@link JavaSource} should be created
281      * @return a new {@link JavaSource}
282      * @throws {@link IllegalArgumentException} if fileObject or cpInfo is null
283      */

284     public static JavaSource create(final ClasspathInfo cpInfo, final FileObject... files) throws IllegalArgumentException JavaDoc {
285         if (files == null || cpInfo == null) {
286             throw new IllegalArgumentException JavaDoc ();
287         }
288         return create(cpInfo, Arrays.asList(files));
289     }
290     
291     private static Map JavaDoc<FileObject, Reference JavaDoc<JavaSource>> file2JavaSource = new WeakHashMap JavaDoc<FileObject, Reference JavaDoc<JavaSource>>();
292     
293     /**
294      * Returns a {@link JavaSource} instance associated to given {@link org.openide.filesystems.FileObject},
295      * it returns null if the {@link Document} is not associanted with data type providing the {@link JavaSource}.
296      * @param fileObject for which the {@link JavaSource} should be found/created.
297      * @return {@link JavaSource} or null
298      * @throws {@link IllegalArgumentException} if fileObject is null
299      */

300     public static JavaSource forFileObject(FileObject fileObject) throws IllegalArgumentException JavaDoc {
301         if (fileObject == null) {
302             throw new IllegalArgumentException JavaDoc ("fileObject == null"); //NOI18N
303
}
304         if (!fileObject.isValid()) {
305             return null;
306         }
307         if (!"text/x-java".equals(FileUtil.getMIMEType(fileObject)) && !"java".equals(fileObject.getExt())) { //NOI18N
308
//TODO: JavaSource cannot be created for all kinds of files, but text/x-java is too restrictive:
309
return null;
310         }
311
312         Reference JavaDoc<JavaSource> ref = file2JavaSource.get(fileObject);
313         JavaSource js = ref != null ? ref.get() : null;
314
315         if (js == null) {
316             file2JavaSource.put(fileObject, new WeakReference JavaDoc<JavaSource>(js = create(ClasspathInfo.create(fileObject), fileObject)));
317         }
318
319         return js;
320     }
321     
322     /**
323      * Returns a {@link JavaSource} instance associated to {@link org.openide.filesystems.FileObject}
324      * the {@link Document} was created from, it returns null if the {@link Document} is not
325      * associanted with data type providing the {@link JavaSource}.
326      * @param doc {@link Document} for which the {@link JavaSource} should be found/created.
327      * @return {@link JavaSource} or null
328      * @throws {@link IllegalArgumentException} if doc is null
329      */

330     public static JavaSource forDocument(Document JavaDoc doc) throws IllegalArgumentException JavaDoc {
331         if (doc == null) {
332             throw new IllegalArgumentException JavaDoc ("doc == null"); //NOI18N
333
}
334         Reference JavaDoc<?> ref = (Reference JavaDoc<?>) doc.getProperty(JavaSource.class);
335         JavaSource js = ref != null ? (JavaSource) ref.get() : null;
336         if (js == null) {
337             DataObject dObj = (DataObject)doc.getProperty(Document.StreamDescriptionProperty);
338             if (dObj != null)
339                 js = forFileObject(dObj.getPrimaryFile());
340         }
341         return js;
342     }
343     
344     /**
345      * Creates a new instance of JavaSource
346      * @param files to create JavaSource for
347      * @param cpInfo classpath info
348      */

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

400     public void runUserActionTask( final CancellableTask<CompilationController> task, final boolean shared) throws IOException JavaDoc {
401         if (task == null) {
402             throw new IllegalArgumentException JavaDoc ("Task cannot be null"); //NOI18N
403
}
404         
405         assert !holdsDocumentWriteLock(files) : "JavaSource.runCompileControlTask called under Document write lock."; //NOI18N
406

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

520     public ModificationResult runModificationTask(CancellableTask<WorkingCopy> task) throws IOException JavaDoc {
521         if (task == null) {
522             throw new IllegalArgumentException JavaDoc ("Task cannot be null"); //NOI18N
523
}
524         
525         assert !holdsDocumentWriteLock(files) : "JavaSource.runModificationTask called under Document write lock."; //NOI18N
526

527         ModificationResult result = new ModificationResult(this);
528         if (this.files.size()<=1) {
529             long start = System.currentTimeMillis();
530             CompilationInfo currentInfo = null;
531             synchronized (this) {
532                 if (this.currentInfo != null && (this.flags & INVALID) == 0) {
533                     currentInfo = this.currentInfo;
534                 }
535             }
536             if (currentInfo == null) {
537                 currentInfo = createCurrentInfo(this,this.files.isEmpty() ? null : this.files.iterator().next(), filterListener, null);
538                 synchronized (this) {
539                     if (this.currentInfo == null || (this.flags & INVALID) != 0) {
540                         this.currentInfo = currentInfo;
541                         this.flags&=~INVALID;
542                     }
543                     else {
544                         currentInfo = this.currentInfo;
545                     }
546                 }
547             }
548             assert currentInfo != null;
549             final JavaSource.Request request = currentRequest.getTaskToCancel();
550             try {
551                 if (request != null) {
552                     request.task.cancel();
553                 }
554                 this.javacLock.lock();
555                 try {
556                     WorkingCopy copy = new WorkingCopy (currentInfo);
557                     task.run (copy);
558                     List JavaDoc<Difference> diffs = copy.getChanges();
559                     if (diffs != null && diffs.size() > 0)
560                         result.diffs.put(currentInfo.getFileObject(), diffs);
561                 } catch (Exception JavaDoc e) {
562                     IOException JavaDoc ioe = new IOException JavaDoc ();
563                     ioe.initCause(e);
564                     throw ioe;
565                 } finally {
566                     this.javacLock.unlock();
567                 }
568             } finally {
569                 currentRequest.cancelCompleted (request);
570             }
571             TimesCollector.getDefault().reportTime(currentInfo.getFileObject(), "java-source-modification-task", //NOI18N
572
"Modification Task", System.currentTimeMillis() - start); //NOI18N
573
}
574         else {
575             final JavaSource.Request request = currentRequest.getTaskToCancel();
576             try {
577                 if (request != null) {
578                     request.task.cancel();
579                 }
580                 this.javacLock.lock();
581                 try {
582                     JavacTaskImpl jt = null;
583                     FileObject activeFile = null;
584                     Iterator JavaDoc<FileObject> files = this.files.iterator();
585                     while (files.hasNext() || activeFile != null) {
586                         boolean restarted;
587                         if (activeFile == null) {
588                             activeFile = files.next();
589                             restarted = false;
590                         }
591                         else {
592                             restarted = true;
593                         }
594                         CompilationInfo ci = createCurrentInfo(this,activeFile, filterListener, jt);
595                         WorkingCopy copy = new WorkingCopy(ci);
596                         task.run(copy);
597                         if (!ci.needsRestart) {
598                             jt = ci.getJavacTask();
599                             Log.instance(jt.getContext()).nerrors = 0;
600                             List JavaDoc<Difference> diffs = copy.getChanges();
601                             if (diffs != null && diffs.size() > 0)
602                                 result.diffs.put(ci.getFileObject(), diffs);
603                             activeFile = null;
604                         }
605                         else {
606                             jt = null;
607                             ci = null;
608                             System.gc();
609                             if (restarted) {
610                                 throw new InsufficientMemoryException (activeFile);
611                             }
612                         }
613                     }
614                 } catch (Exception JavaDoc e) {
615                     IOException JavaDoc ioe = new IOException JavaDoc ();
616                     ioe.initCause(e);
617                     throw ioe;
618                 } finally {
619                     this.javacLock.unlock();
620                 }
621             } finally {
622                 currentRequest.cancelCompleted(request);
623             }
624         }
625         synchronized (this) {
626             this.flags|=INVALID;
627         }
628         return result;
629     }
630        
631     /** Adds a task to given compilation phase. The tasks will run sequentially by
632      * priorty after given phase is reached.
633      * @see CancellableTask for information about implementation requirements
634      * @task The task to run.
635      * @phase In which phase should the task run
636      * @priority Priority of the task.
637      */

638     void addPhaseCompletionTask( CancellableTask<CompilationInfo> task, Phase phase, Priority priority ) throws IOException JavaDoc {
639         if (task == null) {
640             throw new IllegalArgumentException JavaDoc ("Task cannot be null"); //NOI18N
641
}
642         if (phase == null || phase == Phase.MODIFIED) {
643             throw new IllegalArgumentException JavaDoc (String.format("The %s is not a legal value of phase",phase)); //NOI18N
644
}
645         if (priority == null) {
646             throw new IllegalArgumentException JavaDoc ("The priority cannot be null"); //NOI18N
647
}
648         CompilationInfo currentInfo;
649         synchronized (this) {
650             currentInfo = this.currentInfo;
651         }
652         if (currentInfo == null) {
653             currentInfo = createCurrentInfo (this, this.files.isEmpty() ? null : this.files.iterator().next(), filterListener, null);
654         }
655         synchronized (this) {
656             if (this.currentInfo == null) {
657                 this.currentInfo = currentInfo;
658             }
659         }
660         handleAddRequest (new Request (task, this, phase, priority, true));
661     }
662     
663     /** Removes the task from the phase queue.
664      * @task The task to remove.
665      */

666     void removePhaseCompletionTask( CancellableTask<CompilationInfo> task ) {
667         synchronized (JavaSource.class) {
668             toRemove.add (task);
669         }
670     }
671     
672     /**Rerun the task in case it was already run. Does nothing if the task was not already run.
673      *
674      * @task to reschedule
675      */

676     void rescheduleTask(CancellableTask<CompilationInfo> task) {
677         synchronized (JavaSource.class) {
678             JavaSource.Request request = this.currentRequest.getTaskToCancel (task);
679             if ( request == null) {
680 out: for (Iterator JavaDoc<Collection JavaDoc<Request>> it = finishedRequests.values().iterator(); it.hasNext();) {
681                     Collection JavaDoc<Request> cr = it.next ();
682                     for (Iterator JavaDoc<Request> it2 = cr.iterator(); it2.hasNext();) {
683                         Request fr = it2.next();
684                         if (task == fr.task) {
685                             it2.remove();
686                             JavaSource.requests.add(fr);
687                             if (cr.size()==0) {
688                                 it.remove();
689                             }
690                             break out;
691                         }
692                     }
693                 }
694             }
695             else {
696                 currentRequest.cancelCompleted(request);
697             }
698         }
699     }
700         
701     /**
702      * Marks this {@link JavaSource} as modified, causes that the cached information are
703      * cleared and all the PhaseCompletionTasks are restarted.
704      * The only client of this method should be the JavaDataObject or other DataObjects
705      * providing the {@link JavaSource}. If you call this method in another case you are
706      * probably doing something incorrect.
707      */

708     void revalidate () {
709         this.resetState(true, false);
710     }
711     
712     /**
713      * Returns the classpaths ({@link ClasspathInfo}) used by this
714      * {@link JavaSource}
715      * @return {@link ClasspathInfo}, never returns null.
716      */

717     public ClasspathInfo getClasspathInfo() {
718     return classpathInfo;
719     }
720     
721     /**
722      * Returns unmodifiable {@link Collection} of {@link FileObject}s used by this {@link JavaSource}
723      * @return the {@link FileObject}s
724      */

725     public Collection JavaDoc<FileObject> getFileObjects() {
726         return files;
727     }
728     
729     JavacTaskImpl createJavacTask(final DiagnosticListener<? super JavaFileObject> diagnosticListener) {
730         String JavaDoc sourceLevel = null;
731         if (!this.files.isEmpty()) {
732             sourceLevel = SourceLevelQuery.getSourceLevel(files.iterator().next());
733         }
734         if (sourceLevel == null) {
735             sourceLevel = JavaPlatformManager.getDefault().getDefaultPlatform().getSpecification().getVersion().toString();
736         }
737         JavacTaskImpl javacTask = createJavacTask(getClasspathInfo(), diagnosticListener, sourceLevel, false);
738         Context context = javacTask.getContext();
739         Messager.preRegister(context, null);
740         ErrorHandlingJavadocEnter.preRegister(context);
741         JavadocMemberEnter.preRegister(context);
742         JavadocEnv.preRegister(context, getClasspathInfo());
743         Scanner.Factory.instance(context);
744         //Builder2.instance(context).keepComments = true;
745
com.sun.tools.javac.main.JavaCompiler.instance(context).keepComments = true;
746         return javacTask;
747     }
748     
749     private static JavacTaskImpl createJavacTask(final ClasspathInfo cpInfo, final DiagnosticListener<? super JavaFileObject> diagnosticListener, final String JavaDoc sourceLevel, final boolean backgroundCompilation) {
750         ArrayList JavaDoc<String JavaDoc> options = new ArrayList JavaDoc<String JavaDoc>();
751         if (!backgroundCompilation) {
752             if (Boolean.getBoolean("org.netbeans.api.java.source.JavaSource.USE_COMPILER_LINT")) { // XXX temp workaround for #76702
753
options.add("-Xlint");
754                 options.add("-Xlint:-serial");
755             }
756             options.add("-Xjcov"); //NOI18N, Make the compiler store end positions
757
} else {
758             options.add("-XDbackgroundCompilation"); //NOI18N
759
options.add("-XDcompilePolicy=byfile"); //NOI18N
760
}
761         options.add("-g:"); // NOI18N, Enable some debug info
762
options.add("-g:lines"); // NOI18N, Make the compiler to maintain line table
763
options.add("-g:vars"); // NOI18N, Make the compiler to maintain local variables table
764
options.add("-source"); // NOI18N
765
options.add(validateSourceLevel(sourceLevel));
766
767         ClassLoader JavaDoc orig = Thread.currentThread().getContextClassLoader();
768         try {
769             //The ToolProvider.defaultJavaCompiler will use the context classloader to load the javac implementation
770
//it should be load by the current module's classloader (should delegate to other module's classloaders as necessary)
771
Thread.currentThread().setContextClassLoader(ClasspathInfo.class.getClassLoader());
772             JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
773             JavacTaskImpl task = (JavacTaskImpl)tool.getTask(null, cpInfo.getFileManager(), diagnosticListener, options, null, Collections.<JavaFileObject>emptySet());
774             Context context = task.getContext();
775             
776             if (backgroundCompilation) {
777                 SymbolClassReader.preRegister(context, false);
778             } else {
779                 SymbolClassReader.preRegister(context, true);
780             }
781             
782             return task;
783         } finally {
784             Thread.currentThread().setContextClassLoader(orig);
785         }
786     }
787      
788     private static class ErrorHandlingJavadocEnter extends JavadocEnter {
789         
790         private Messager messager;
791         
792         public static void preRegister(final Context context) {
793             context.put(enterKey, new Context.Factory<Enter>() {
794                 public Enter make() {
795                     return new ErrorHandlingJavadocEnter(context);
796                 }
797             });
798         }
799         
800         protected ErrorHandlingJavadocEnter(Context context) {
801             super(context);
802             messager = Messager.instance0(context);
803         }
804         
805         public @Override JavaDoc void main(com.sun.tools.javac.util.List<JCCompilationUnit> trees) {
806             //Todo: Check everytime after the java update that JavaDocEnter.main or Enter.main
807
//are not changed.
808
this.complete(trees, null);
809         }
810     }
811
812     /**
813      * Not synchronized, only the CompilationJob's thread can call it!!!!
814      *
815      */

816     static Phase moveToPhase (final Phase phase, final CompilationInfo currentInfo, final boolean cancellable) throws IOException JavaDoc {
817         Phase currentPhase = currentInfo.getPhase();
818         final boolean isMultiFiles = currentInfo.getJavaSource().files.size()>1;
819         LowMemoryNotifier lm = null;
820         LMListener lmListener = null;
821         if (isMultiFiles) {
822             lm = LowMemoryNotifier.getDefault();
823             assert lm != null;
824             lmListener = new LMListener ();
825             lm.addLowMemoryListener (lmListener);
826         }
827         try {
828             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
829                 currentInfo.needsRestart = true;
830                 return currentPhase;
831             }
832             if (currentPhase.compareTo(Phase.PARSED)<0 && phase.compareTo(Phase.PARSED)>=0) {
833                 if (cancellable && currentRequest.isCanceled()) {
834                     //Keep the currentPhase unchanged, it may happen that an userActionTask
835
//runnig after the phace completion task may still use it.
836
return Phase.MODIFIED;
837                 }
838                 long start = System.currentTimeMillis();
839                 // XXX - this might be with wrong encoding
840
Iterable JavaDoc<? extends CompilationUnitTree> trees = currentInfo.getJavacTask().parse(new JavaFileObject[] {currentInfo.jfo});
841                 assert trees != null : "Did not parse anything"; //NOI18N
842
Iterator JavaDoc<? extends CompilationUnitTree> it = trees.iterator();
843                 assert it.hasNext();
844                 CompilationUnitTree unit = it.next();
845                 currentInfo.setCompilationUnit(unit);
846                 JCCompilationUnit tree = (JCCompilationUnit) unit;
847                 ASTService s = ASTService.instance(currentInfo.getJavacTask().getContext());
848                 if (tree.endPositions != null) {
849                     s.setEndPosTable(tree.sourcefile, tree.endPositions);
850                 }
851                 List JavaDoc<CompilationUnitTree> units = new ArrayList JavaDoc<CompilationUnitTree>();
852                 units.add(tree);
853                 s.setRoot(new RootTree(units));
854                 UndoListService undoList = UndoListService.instance(currentInfo.getJavacTask().getContext());
855                 if (undoList != null) {
856                     undoList.reset(); //setRoot above created an undo event - clear it
857
}
858                 assert !it.hasNext();
859                 currentPhase = Phase.PARSED;
860                 long end = System.currentTimeMillis();
861                 FileObject file = currentInfo.getFileObject();
862                 TimesCollector.getDefault().reportReference(file, "compilationUnit", "[M] Compilation Unit", unit); //NOI18N
863
logTime (file,currentPhase,(end-start));
864             }
865             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
866                 currentInfo.needsRestart = true;
867                 return currentPhase;
868             }
869             if (currentPhase == Phase.PARSED && phase.compareTo(Phase.ELEMENTS_RESOLVED)>=0) {
870                 if (cancellable && currentRequest.isCanceled()) {
871                     return Phase.MODIFIED;
872                 }
873                 long start = System.currentTimeMillis();
874                 currentInfo.getJavacTask().enter();
875                 currentPhase = Phase.ELEMENTS_RESOLVED;
876                 long end = System.currentTimeMillis();
877                 logTime(currentInfo.getFileObject(),currentPhase,(end-start));
878            }
879             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
880                 currentInfo.needsRestart = true;
881                 return currentPhase;
882             }
883             if (currentPhase == Phase.ELEMENTS_RESOLVED && phase.compareTo(Phase.RESOLVED)>=0) {
884                 if (cancellable && currentRequest.isCanceled()) {
885                     return Phase.MODIFIED;
886                 }
887                 long start = System.currentTimeMillis ();
888                 currentInfo.getJavacTask().analyze();
889                 currentPhase = Phase.RESOLVED;
890                 long end = System.currentTimeMillis ();
891                 logTime(currentInfo.getFileObject(),currentPhase,(end-start));
892             }
893             if (lmListener != null && lmListener.lowMemory.getAndSet(false)) {
894                 currentInfo.needsRestart = true;
895                 return currentPhase;
896             }
897             if (currentPhase == Phase.RESOLVED && phase.compareTo(Phase.UP_TO_DATE)>=0) {
898                 currentPhase = Phase.UP_TO_DATE;
899             }
900         } catch (CouplingAbort a) {
901             RepositoryUpdater.couplingAbort(a, currentInfo.jfo);
902             currentInfo.needsRestart = true;
903             return currentPhase;
904         } catch (Abort abort) {
905             currentPhase = Phase.UP_TO_DATE;
906         } catch (IOException JavaDoc ex) {
907             dumpSource(currentInfo, ex);
908             throw ex;
909         } catch (ReattributionException ex) {
910             dumpSource(currentInfo, ex);
911             throw new RuntimeException JavaDoc(ex);
912         } catch (RuntimeException JavaDoc ex) {
913             dumpSource(currentInfo, ex);
914             throw ex;
915         } catch (Error JavaDoc ex) {
916             dumpSource(currentInfo, ex);
917             throw ex;
918         }
919         finally {
920             if (isMultiFiles) {
921                 assert lm != null;
922                 assert lmListener != null;
923                 lm.removeLowMemoryListener (lmListener);
924             }
925             currentInfo.setPhase(currentPhase);
926         }
927         return currentPhase;
928     }
929     
930     static void logTime (FileObject source, Phase phase, long time) {
931         assert source != null && phase != null;
932         String JavaDoc key = phase2Key.get(phase);
933         String JavaDoc message = phase2Message.get(phase);
934         assert key != null && message != null;
935         TimesCollector.getDefault().reportTime (source,key,message,time);
936     }
937     
938     private static final RequestProcessor RP = new RequestProcessor ("JavaSource-event-collector",1); //NOI18N
939

940     private final RequestProcessor.Task resetTask = RP.create(new Runnable JavaDoc() {
941         public void run() {
942             resetStateImpl();
943         }
944     });
945     
946     private void resetState(boolean invalidate, boolean updateIndex) {
947         boolean invalid;
948         synchronized (this) {
949             invalid = (this.flags & INVALID) != 0;
950             this.flags|=CHANGE_EXPECTED;
951             if (invalidate) {
952                 this.flags|=(INVALID|RESCHEDULE_FINISHED_TASKS);
953             }
954             if (updateIndex) {
955                 this.flags|=UPDATE_INDEX;
956             }
957         }
958         if (updateIndex && !invalid) {
959             //First change set the index as dirty
960
updateIndex ();
961         }
962         Request r = currentRequest.getTaskToCancel (this);
963         try {
964             if (r != null) {
965                 r.task.cancel();
966             }
967         }
968         finally {
969             currentRequest.cancelCompleted(r);
970         }
971         resetTask.schedule(REPARSE_DELAY);
972     }
973     
974     /**
975      * Not synchronized, only sets the atomic state and clears the listeners
976      *
977      */

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

1619    static interface JavaFileObjectProvider {
1620        public JavaFileObject createJavaFileObject (FileObject fo, JavaFileFilterImplementation filter) throws IOException JavaDoc;
1621    }
1622    
1623    static final class DefaultJavaFileObjectProvider implements JavaFileObjectProvider {
1624        public JavaFileObject createJavaFileObject (FileObject fo, JavaFileFilterImplementation filter) throws IOException JavaDoc {
1625            return FileObjects.nbFileObject(fo, filter, true);
1626        }
1627    }
1628    
1629    private static class LMListener implements LowMemoryListener {
1630        private AtomicBoolean JavaDoc lowMemory = new AtomicBoolean JavaDoc (false);
1631        
1632        public void lowMemory(LowMemoryEvent event) {
1633            lowMemory.set(true);
1634        }
1635    }
1636    
1637    /**
1638     *Ugly and slow, called only when -ea
1639     *
1640     */

1641    private static boolean holdsDocumentWriteLock (Iterable JavaDoc<FileObject> files) {
1642        final Class JavaDoc<AbstractDocument JavaDoc> docClass = AbstractDocument JavaDoc.class;
1643        try {
1644            final Method JavaDoc method = docClass.getDeclaredMethod("getCurrentWriter"); //NOI18N
1645
method.setAccessible(true);
1646            final Thread JavaDoc currentThread = Thread.currentThread();
1647            for (FileObject fo : files) {
1648                try {
1649                final DataObject dobj = DataObject.find(fo);
1650                final EditorCookie ec = dobj.getCookie(EditorCookie.class);
1651                if (ec != null) {
1652                    final StyledDocument JavaDoc doc = ec.getDocument();
1653                    if (doc instanceof AbstractDocument JavaDoc) {
1654                        Object JavaDoc result = method.invoke(doc);
1655                        if (result == currentThread) {
1656                            return true;
1657                        }
1658                    }
1659                }
1660                } catch (Exception JavaDoc e) {
1661                    Exceptions.printStackTrace(e);
1662                }
1663            }
1664        } catch (NoSuchMethodException JavaDoc e) {
1665            Exceptions.printStackTrace(e);
1666        }
1667        return false;
1668    }
1669    
1670    private static String JavaDoc validateSourceLevel (String JavaDoc sourceLevel) {
1671        Source[] sources = Source.values();
1672        if (sourceLevel == null) {
1673            //Should never happen but for sure
1674
return sources[sources.length-1].name;
1675        }
1676        for (Source source : sources) {
1677            if (source.name.equals(sourceLevel)) {
1678                return sourceLevel;
1679            }
1680        }
1681        SpecificationVersion specVer = new SpecificationVersion (sourceLevel);
1682        SpecificationVersion JAVA_12 = new SpecificationVersion ("1.2"); //NOI18N
1683
if (JAVA_12.compareTo(specVer)>0) {
1684            //Some SourceLevelQueries return 1.1 source level which is invalid, use 1.2
1685
return sources[0].name;
1686        }
1687        else {
1688            return sources[sources.length-1].name;
1689        }
1690    }
1691    
1692    private static final int MAX_DUMPS = 255;
1693    
1694    /**
1695     * Dumps the source code to the file. Used for parser debugging. Only a limited number
1696     * of dump files is used. If the last file exists, this method doesn't dump anything.
1697     *
1698     * @param info CompilationInfo for which the error occurred.
1699     * @param exc exception to write to the end of dump file
1700     */

1701    private static void dumpSource(CompilationInfo info, Throwable JavaDoc exc) {
1702        String JavaDoc dumpDir = System.getProperty("netbeans.user") + "/var/log/"; //NOI18N
1703
String JavaDoc src = info.getText();
1704        FileObject file = info.getFileObject();
1705        String JavaDoc fileName = FileUtil.getFileDisplayName(info.getFileObject());
1706        String JavaDoc origName = file.getName();
1707        File JavaDoc f = new File JavaDoc(dumpDir + origName + ".dump"); // NOI18N
1708
boolean dumpSucceeded = false;
1709        int i = 1;
1710        while (i < MAX_DUMPS) {
1711            if (!f.exists())
1712                break;
1713            f = new File JavaDoc(dumpDir + origName + '_' + i + ".dump"); // NOI18N
1714
i++;
1715        }
1716        if (!f.exists()) {
1717            try {
1718                OutputStream JavaDoc os = new FileOutputStream JavaDoc(f);
1719                PrintWriter JavaDoc writer = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(os, "UTF-8")); // NOI18N
1720
try {
1721                    writer.println(src);
1722                    writer.println("----- Classpath: ---------------------------------------------"); // NOI18N
1723

1724                    final ClassPath bootPath = info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.BOOT);
1725                    final ClassPath classPath = info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.COMPILE);
1726                    final ClassPath sourcePath = info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.SOURCE);
1727
1728                    writer.println("bootPath: " + (bootPath != null ? bootPath.toString() : "null"));
1729                    writer.println("classPath: " + (classPath != null ? classPath.toString() : "null"));
1730                    writer.println("sourcePath: " + (sourcePath != null ? sourcePath.toString() : "null"));
1731                    
1732                    writer.println("----- Original exception ---------------------------------------------"); // NOI18N
1733
exc.printStackTrace(writer);
1734                } finally {
1735                    writer.close();
1736                    dumpSucceeded = true;
1737                }
1738            } catch (IOException JavaDoc ioe) {
1739                Logger.getLogger("global").log(Level.INFO, "Error when writing parser dump file!", ioe); // NOI18N
1740
}
1741        }
1742        if (dumpSucceeded) {
1743            Throwable JavaDoc t = Exceptions.attachMessage(exc, "An error occurred during parsing of \'" + fileName + "\'. Please report a bug against java/source and attach dump file '" // NOI18N
1744
+ f.getAbsolutePath() + "'."); // NOI18N
1745
Exceptions.printStackTrace(t);
1746        } else {
1747            Logger.getLogger("global").log(Level.WARNING,
1748                    "Dump could not be written. Either dump file could not " + // NOI18N
1749
"be created or all dump files were already used. Please " + // NOI18N
1750
"check that you have write permission to '" + dumpDir + "' and " + // NOI18N
1751
"clean all *.dump files in that directory."); // NOI18N
1752
}
1753    }
1754    
1755}
1756
Popular Tags