1 19 20 package org.netbeans.api.retouche.source; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.io.File ; 25 import java.io.FileOutputStream ; 26 import java.io.IOException ; 27 import java.io.OutputStream ; 28 import java.io.OutputStreamWriter ; 29 import java.io.PrintWriter ; 30 import java.lang.ref.Reference ; 31 import java.lang.ref.WeakReference ; 32 import java.lang.reflect.Method ; 33 import java.util.ArrayList ; 34 import java.util.Arrays ; 35 import java.util.Collection ; 36 import java.util.Collections ; 37 import java.util.Comparator ; 38 import java.util.HashMap ; 39 import java.util.HashSet ; 40 import java.util.Iterator ; 41 import java.util.LinkedList ; 42 import java.util.List ; 43 import java.util.Map ; 44 import java.util.WeakHashMap ; 45 import java.util.concurrent.Executors ; 46 import java.util.concurrent.PriorityBlockingQueue ; 47 import java.util.concurrent.ThreadFactory ; 48 import java.util.concurrent.TimeUnit ; 49 import java.util.concurrent.atomic.AtomicBoolean ; 50 import java.util.concurrent.locks.ReentrantLock ; 51 import java.util.logging.Level ; 52 import java.util.logging.Logger ; 53 import javax.swing.event.ChangeEvent ; 54 import javax.swing.event.ChangeListener ; 55 import javax.swing.event.DocumentEvent ; 56 import javax.swing.event.DocumentListener ; 57 import javax.swing.text.AbstractDocument ; 58 import javax.swing.text.Document ; 59 import javax.swing.text.StyledDocument ; 60 import javax.swing.event.CaretEvent ; 61 import javax.swing.event.CaretListener ; 62 import javax.swing.event.ChangeListener ; 63 import javax.swing.text.JTextComponent ; 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.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 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 139 public static final class InsufficientMemoryException extends IOException { 140 141 private FileObject fo; 142 143 private InsufficientMemoryException (final String 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 157 public FileObject getFile () { 158 return this.fo; 159 } 160 } 161 162 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 171 private static final boolean reportSlowTasks = Boolean.getBoolean("org.netbeans.api.retouche.source.Source.reportSlowTasks"); 173 private static final int SLOW_TASK_LIMIT = 250; 174 private static final int SLOW_CANCEL_LIMIT = 50; 175 176 177 static int REPARSE_DELAY = 500; 178 179 183 private static Map <Phase, String > phase2Key = new HashMap <Phase, String > (); 184 private static Map <Phase, String > phase2Message = new HashMap <Phase, String > (); 185 186 189 static { 190 SourceAccessor.INSTANCE = new JavaSourceAccessorImpl (); 191 phase2Key.put(Phase.PARSED,"parsed"); phase2Message.put (Phase.PARSED,"Parsed"); 194 phase2Key.put(Phase.ELEMENTS_RESOLVED,"sig-attributed"); phase2Message.put (Phase.ELEMENTS_RESOLVED,"Signatures Attributed"); 197 phase2Key.put(Phase.RESOLVED, "attributed"); phase2Message.put (Phase.RESOLVED, "Attributed"); 200 } 201 202 private final static PriorityBlockingQueue <Request> requests = new PriorityBlockingQueue <Request> (10, new RequestComparator()); 203 private final static Map <Source, Collection <Request>> finishedRequests = new WeakHashMap <Source,Collection <Request>>(); 204 private final static Map <Source,Collection <Request>> waitingRequests = new WeakHashMap <Source,Collection <Request>>(); 205 private final static Collection <CancellableTask> toRemove = new LinkedList <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 private final static ReentrantLock javacLock = new ReentrantLock (true); 211 212 private final Collection <FileObject> files; 213 private final FileObject rootFo; 214 private final FileChangeListener fileChangeListener; 215 private DocListener listener; 216 private DataObjectListener dataObjectListener; 217 private String sourceLevel; 218 219 private final ClasspathInfo classpathInfo; 220 private CompilationInfo currentInfo; 221 private java.util.Stack <CompilationInfo> infoStack = new java.util.Stack <CompilationInfo> (); 222 223 private int flags = 0; 224 225 static { 226 Executors.newSingleThreadExecutor(factory).submit (new CompilationJob()); 227 } 228 229 230 240 public static Source create(final ClasspathInfo cpInfo, final Collection <? extends FileObject> files) throws IllegalArgumentException { 241 if (files == null || cpInfo == null) { 242 throw new IllegalArgumentException (); 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())); } catch (IOException ex) { 249 Exceptions.printStackTrace(ex); 250 } 251 return null; 252 } 253 254 255 265 public static Source create(final ClasspathInfo cpInfo, final FileObject... files) throws IllegalArgumentException { 266 if (files == null || cpInfo == null) { 267 throw new IllegalArgumentException (); 268 } 269 return create(cpInfo, Arrays.asList(files)); 270 } 271 272 private static Map <FileObject, Reference <Source>> file2JavaSource = new WeakHashMap <FileObject, Reference <Source>>(); 273 274 283 public static Source forFileObject(FileObject fileObject) throws IllegalArgumentException { 284 if (fileObject == null) { 285 throw new IllegalArgumentException ("fileObject == null"); } 287 if (!fileObject.isValid()) { 288 return null; 289 } 290 if (!LanguageRegistry.getInstance().isSupported(fileObject.getMIMEType())) { 295 return null; 296 } 297 298 Reference <Source> ref = file2JavaSource.get(fileObject); 299 Source js = ref != null ? ref.get() : null; 300 301 if (js == null) { 302 file2JavaSource.put(fileObject, new WeakReference (js = create(ClasspathInfo.create(fileObject), fileObject))); 303 } 304 305 return js; 306 } 307 308 318 public static Source forDocument(Document doc) throws IllegalArgumentException { 319 if (doc == null) { 320 throw new IllegalArgumentException ("doc == null"); } 322 Reference <Source> ref = (Reference <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 340 private Source (ClasspathInfo cpInfo, Collection <? extends FileObject> files) throws IOException { 341 this.files = Collections.unmodifiableList(new ArrayList (files)); this.fileChangeListener = new FileChangeListenerImpl (); 343 boolean multipleSources = this.files.size() > 1; 344 for (Iterator <? extends FileObject> it = this.files.iterator(); it.hasNext();) { 345 FileObject file = it.next(); 346 try { 347 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)); 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 386 public void runUserActionTask( final CancellableTask<CompilationController> task, final boolean shared) throws IOException { 387 if (task == null) { 388 throw new IllegalArgumentException ("Task cannot be null"); } 390 391 assert !holdsDocumentWriteLock(files) : "Source.runCompileControlTask called under Document write lock."; 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 e) { 441 IOException ioe = new IOException (); 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 <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 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 e) { 488 IOException ioe = new IOException (); 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 506 public ModificationResult runModificationTask(CancellableTask<WorkingCopy> task) throws IOException { 507 if (task == null) { 508 throw new IllegalArgumentException ("Task cannot be null"); } 510 511 assert !holdsDocumentWriteLock(files) : "Source.runModificationTask called under Document write lock."; 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 <Difference> diffs = copy.getChanges(); 544 if (diffs != null && diffs.size() > 0) 545 result.diffs.put(currentInfo.getFileObject(), diffs); 546 } catch (Exception e) { 547 IOException ioe = new IOException (); 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 <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 List <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 e) { 598 IOException ioe = new IOException (); 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 618 void addPhaseCompletionTask( CancellableTask<CompilationInfo> task, Phase phase, Priority priority ) throws IOException { 619 if (task == null) { 620 throw new IllegalArgumentException ("Task cannot be null"); } 622 if (phase == null || phase == Phase.MODIFIED) { 623 throw new IllegalArgumentException (String.format("The %s is not a legal value of phase",phase)); } 625 if (priority == null) { 626 throw new IllegalArgumentException ("The priority cannot be null"); } 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 646 void removePhaseCompletionTask( CancellableTask<CompilationInfo> task ) { 647 synchronized (Source.class) { 648 toRemove.add (task); 649 } 650 } 651 652 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 <Collection <Request>> it = finishedRequests.values().iterator(); it.hasNext();) { 661 Collection <Request> cr = it.next (); 662 for (Iterator <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 688 void revalidate () { 689 this.resetState(true, false); 690 } 691 692 699 public ClasspathInfo getClasspathInfo() { 700 return classpathInfo; 701 } 702 703 707 public Collection <FileObject> getFileObjects() { 708 return files; 709 } 710 711 ParserTaskImpl createParserTask( CompilationInfo compilationInfo) { 712 Language language = LanguageRegistry.getInstance().getLanguageByMimeType(compilationInfo.getFileObject().getMIMEType()); 714 assert language != null; 715 ParserTaskImpl javacTask = createParserTask(language, compilationInfo, getClasspathInfo(), sourceLevel, false); 716 return javacTask; 724 } 725 726 private static ParserTaskImpl createParserTask(Language language, final CompilationInfo currentInfo, final ClasspathInfo cpInfo, final String sourceLevel, final boolean backgroundCompilation) { 727 ArrayList <String > options = new ArrayList <String >(); 728 729 730 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 748 static Phase moveToPhase (final Phase phase, final CompilationInfo currentInfo, final boolean cancellable) throws IOException { 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 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(); if (parser != null) { 776 final String 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 error) { 784 currentInfo.addError(error); 785 } 786 787 public void exception(Exception exception) { 788 Exceptions.printStackTrace(exception); 789 } 790 791 public void finished(ParseEvent e) { 792 if (e.getKind() == ParseEvent.Kind.PARSE) { 794 resultHolder[0] = e.getResult(); 795 } 796 } 797 798 }; 799 List <ParserFile> sourceFiles = new ArrayList <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 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 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 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 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 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 (RuntimeException ex) { 880 dumpSource(currentInfo, ex); 881 throw ex; 882 } catch (java.lang.Error 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 key = phase2Key.get(phase); 900 String message = phase2Message.get(phase); 901 assert key != null && message != null; 902 } 904 905 private final RequestProcessor.Task resetTask = RequestProcessor.getDefault().create(new Runnable () { 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 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 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 updateIndex (); 955 } 956 Collection <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 ioe) { 976 Exceptions.printStackTrace(ioe); 977 } 978 } 979 } 980 981 private void assignDocumentListener(FileObject fo) throws IOException { 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))); } 989 } 990 991 private static class Request { 992 private final CancellableTask<? extends CompilationInfo> task; 993 private final Source javaSource; 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 String toString () { 1009 if (reschedule) { 1010 return String.format("Periodic request for phase: %s with priority: %s to perform: %s", phase.name(), priority, task.toString()); } 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()); } 1015 } 1016 1017 public @Override int hashCode () { 1018 return this.priority.ordinal(); 1019 } 1020 1021 public @Override boolean equals (Object 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 <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 { 1043 1044 @SuppressWarnings ("unchecked") public void run () { 1046 try { 1047 while (true) { 1048 try { 1049 synchronized (Source.class) { 1050 if (!toRemove.isEmpty()) { 1052 for (Iterator <Collection <Request>> it = finishedRequests.values().iterator(); it.hasNext();) { 1053 Collection <Request> cr = it.next (); 1054 for (Iterator <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 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 if (toRemove.remove(r.task)) { 1093 continue; 1094 } 1095 synchronized (js) { 1096 boolean changeExpected = (js.flags & CHANGE_EXPECTED) != 0; 1097 if (changeExpected) { 1098 Collection <Request> rc = Source.waitingRequests.get (r.javaSource); 1100 if (rc == null) { 1101 rc = new LinkedList <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 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 try { 1137 final long startTime = System.currentTimeMillis(); 1138 ((CancellableTask<CompilationInfo>)r.task).run (ci); 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.", 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.", r.task.getClass().toString(), (endTime-cancelTime))); 1149 } 1150 } 1151 } catch (RuntimeException 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 Source.requests.add(r); 1167 } 1168 else { 1169 Collection <Request> rc = Source.finishedRequests.get (r.javaSource); 1171 if (rc == null) { 1172 rc = new LinkedList <Request> (); 1173 Source.finishedRequests.put (r.javaSource, rc); 1174 } 1175 rc.add(r); 1176 } 1177 } 1178 } 1179 } 1180 } catch (final IOException invalidFile) { 1181 } 1184 } 1185 } finally { 1186 currentRequest.setCurrentTask(null); 1187 } 1188 } 1189 } catch (Throwable e) { 1190 if (e instanceof InterruptedException ) { 1191 throw (InterruptedException )e; 1192 } 1193 else if (e instanceof ThreadDeath ) { 1194 throw (ThreadDeath )e; 1195 } 1196 else { 1197 Exceptions.printStackTrace(e); 1198 } 1199 } 1200 } 1201 } catch (InterruptedException ie) { 1202 ie.printStackTrace(); 1203 } 1205 } 1206 } 1207 1208 private class DocListener implements DocumentListener , PropertyChangeListener , ChangeListener { 1209 1210 private EditorCookie.Observable ec; 1211 private DocumentListener docListener; 1212 1213 public DocListener (EditorCookie.Observable ec) { 1214 assert ec != null; 1215 this.ec = ec; 1216 this.ec.addPropertyChangeListener((PropertyChangeListener )WeakListeners.propertyChange(this, this.ec)); 1217 Document doc = ec.getDocument(); 1218 if (doc != null) { 1219 doc.addDocumentListener(docListener = WeakListeners.create(DocumentListener .class, this, doc)); 1220 } 1221 } 1222 1223 public void insertUpdate(DocumentEvent e) { 1224 Source.this.resetState(true, true); 1228 } 1229 1230 public void removeUpdate(DocumentEvent e) { 1231 Source.this.resetState(true, true); 1235 } 1236 1237 public void changedUpdate(DocumentEvent e) { 1238 } 1239 1240 public void propertyChange(PropertyChangeEvent evt) { 1241 if (EditorCookie.Observable.PROP_DOCUMENT.equals(evt.getPropertyName())) { 1242 Object old = evt.getOldValue(); 1243 if (old instanceof Document && docListener != null) { 1244 ((Document ) old).removeDocumentListener(docListener); 1245 docListener = null; 1246 } 1247 Document doc = ec.getDocument(); 1248 if (doc != null) { 1249 doc.addDocumentListener(docListener = WeakListeners.create(DocumentListener .class, this, doc)); 1250 } 1251 } 1252 } 1253 1254 public void stateChanged(ChangeEvent e) { 1255 Source.this.resetState(true, false); 1256 } 1257 1258 } 1259 1260 private static class EditorRegistryListener implements ChangeListener , CaretListener { 1261 1262 private JTextComponent lastEditor; 1263 1264 public EditorRegistryListener () { 1265 Registry.addChangeListener(this); 1266 } 1267 1268 public void stateChanged(ChangeEvent event) { 1269 final JTextComponent 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 event) { 1282 if (lastEditor != null) { 1283 Document 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 void fileChanged(final FileEvent fe) { 1297 Source.this.resetState(true, false); 1298 } 1299 1300 public @Override void fileRenamed(FileRenameEvent fe) { 1301 Source.this.resetState(true, false); 1302 } 1303 } 1304 1305 private final class DataObjectListener implements PropertyChangeListener { 1306 1307 private DataObject dobj; 1308 private final FileObject fobj; 1309 private PropertyChangeListener 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 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 try { 1334 dobj = DataObject.find(fobj); 1335 dobj.addPropertyChangeListener(wlistener); 1336 assignDocumentListener(fobj); 1337 resetState(true, true); 1338 } catch (IOException ex) { 1339 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 { 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 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 { 1374 1375 private Thread t; 1376 1377 public Thread newThread(Runnable r) { 1378 assert this.t == null; 1379 this.t = new Thread (r,"GSF Source Worker Thread"); return this.t; 1381 } 1382 1383 public boolean isDispatchThread (Thread t) { 1384 assert t != null; 1385 return this.t == t; 1386 } 1387 } 1388 1389 private static class JavaSourceAccessorImpl extends SourceAccessor { 1390 1391 protected @Override void runSpecialTaskImpl (CancellableTask<CompilationInfo> task, Priority priority) { 1392 handleAddRequest(new Request (task, null, null, priority, false)); 1393 } 1394 1395 @Override 1396 public ParserTaskImpl createParserTask(Language language, ClasspathInfo cpInfo, String sourceLevel) { 1397 if (sourceLevel == null) 1398 sourceLevel = JavaPlatformManager.getDefault().getDefaultPlatform().getSpecification().getVersion().toString(); 1399 boolean backgroundCompilation = true; return Source.createParserTask(language, null, cpInfo, sourceLevel, backgroundCompilation); 1403 } 1404 1405 @Override 1406 public ParserTaskImpl getParserTask (final CompilationInfo compilationInfo) { 1407 assert compilationInfo != null; 1408 return compilationInfo.getParserTask(); 1409 } 1410 1411 @Override 1412 public CompilationInfo getCurrentCompilationInfo (final Source js, final Phase phase) throws IOException { 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 1429 public void revalidate(Source js) { 1430 js.revalidate(); 1431 } 1432 1433 @Override 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 { 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 lowMemory = new AtomicBoolean (false); 1559 1560 public void lowMemory(LowMemoryEvent event) { 1561 lowMemory.set(true); 1562 } 1563 } 1564 1565 1569 private static boolean holdsDocumentWriteLock (Iterable <FileObject> files) { 1570 final Class <AbstractDocument > docClass = AbstractDocument .class; 1571 try { 1572 final Method method = docClass.getDeclaredMethod("getCurrentWriter"); method.setAccessible(true); 1574 final Thread 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 doc = ec.getDocument(); 1581 if (doc instanceof AbstractDocument ) { 1582 Object result = method.invoke(doc); 1583 if (result == currentThread) { 1584 return true; 1585 } 1586 } 1587 } 1588 } catch (Exception e) { 1589 Exceptions.printStackTrace(e); 1590 } 1591 } 1592 } catch (NoSuchMethodException e) { 1593 Exceptions.printStackTrace(e); 1594 } 1595 return false; 1596 } 1597 1598 private static final int MAX_DUMPS = 255; 1599 1600 1607 private static void dumpSource(CompilationInfo info, Throwable exc) { 1608 String dumpDir = System.getProperty("netbeans.user") + "/var/log/"; String src = info.getText(); 1610 FileObject file = info.getFileObject(); 1611 String fileName = FileUtil.getFileDisplayName(info.getFileObject()); 1612 String origName = file.getName(); 1613 File f = new File (dumpDir + origName + ".dump"); boolean dumpSucceeded = false; 1615 int i = 1; 1616 while (i < MAX_DUMPS) { 1617 if (!f.exists()) 1618 break; 1619 f = new File (dumpDir + origName + '_' + i + ".dump"); i++; 1621 } 1622 if (!f.exists()) { 1623 try { 1624 OutputStream os = new FileOutputStream (f); 1625 PrintWriter writer = new PrintWriter (new OutputStreamWriter (os, "UTF-8")); try { 1627 writer.println(src); 1628 writer.println("----- Classpath: ---------------------------------------------"); 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 ---------------------------------------------"); exc.printStackTrace(writer); 1640 } finally { 1641 writer.close(); 1642 dumpSucceeded = true; 1643 } 1644 } catch (IOException ioe) { 1645 Logger.getLogger("global").log(Level.INFO, "Error when writing parser dump file!", ioe); } 1647 } 1648 if (dumpSucceeded) { 1649 Throwable t = Exceptions.attachMessage(exc, "An error occurred during parsing of \'" + fileName + "\'. Please report a bug against scripting/ruby and attach dump file '" + f.getAbsolutePath() + "'."); Exceptions.printStackTrace(t); 1652 } else { 1653 Logger.getLogger("global").log(Level.WARNING, 1654 "Dump could not be written. Either dump file could not " + "be created or all dump files were already used. Please " + "check that you have write permission to '" + dumpDir + "' and " + "clean all *.dump files in that directory."); } 1659 } 1660} 1661 | Popular Tags |