Clover coverage report - DrJava Test Coverage (drjava-20110828-r5448)
Coverage timestamp: Sun Aug 28 2011 03:13:33 CDT
file stats: LOC: 618   Methods: 40
NCLOC: 362   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
DefaultJavadocModel.java 9.1% 9.3% 17.5% 10.3%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.model.javadoc;
 38   
 39    import java.io.File;
 40    import java.io.IOException;
 41    import java.io.BufferedReader;
 42    import java.io.StringReader;
 43    import java.util.ArrayList;
 44    import java.util.List;
 45    import java.util.Properties;
 46    import java.awt.EventQueue;
 47   
 48    import edu.rice.cs.plt.lambda.Thunk;
 49    import edu.rice.cs.plt.io.IOUtil;
 50    import edu.rice.cs.plt.concurrent.JVMBuilder;
 51    import edu.rice.cs.plt.concurrent.ConcurrentUtil;
 52    import edu.rice.cs.plt.iter.IterUtil;
 53    import edu.rice.cs.plt.text.TextUtil;
 54    import edu.rice.cs.drjava.model.DJError;
 55    import edu.rice.cs.drjava.model.FileSaveSelector;
 56    import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
 57    import edu.rice.cs.drjava.model.GlobalModel;
 58    import edu.rice.cs.drjava.model.FileMovedException;
 59    import edu.rice.cs.drjava.model.definitions.InvalidPackageException;
 60   
 61    import edu.rice.cs.drjava.DrJava;
 62    import edu.rice.cs.drjava.config.Configuration;
 63    import edu.rice.cs.drjava.config.OptionConstants;
 64    import edu.rice.cs.drjava.model.DrJavaFileUtils;
 65    import edu.rice.cs.drjava.model.compiler.CompilerErrorModel;
 66    import edu.rice.cs.drjava.model.compiler.CompilerListener;
 67    import edu.rice.cs.drjava.model.compiler.DummyCompilerListener;
 68   
 69    import edu.rice.cs.util.swing.Utilities;
 70   
 71    import edu.rice.cs.util.ArgumentTokenizer;
 72    import edu.rice.cs.util.DirectorySelector;
 73    import edu.rice.cs.util.FileOps;
 74    import edu.rice.cs.util.OperationCanceledException;
 75   
 76    import static edu.rice.cs.plt.debug.DebugUtil.error;
 77   
 78    /** Default implementation of JavadocModel interface; generates Javadoc HTML files for a set of documents.
 79    * @version $Id: DefaultJavadocModel.java 5379 2010-08-27 02:53:50Z mgricken $
 80    */
 81    public class DefaultJavadocModel implements JavadocModel {
 82   
 83    /** Used by CompilerErrorModel to open documents that have errors. */
 84    private GlobalModel _model;
 85   
 86    /**Manages listeners to this model. */
 87    private final JavadocEventNotifier _notifier = new JavadocEventNotifier();
 88   
 89    /** Launcher for javadoc process */
 90    private final JVMBuilder _jvmBuilder;
 91   
 92    /** The error model containing all current Javadoc errors. */
 93    private CompilerErrorModel _javadocErrorModel;
 94   
 95    /** Main constructor.
 96    * @param model Source of documents for this JavadocModel
 97    * @param javaCommand Location of the java command to use ({@code null} means the default: {@code java.home})
 98    * @param toolsPath Location of the tools library containing the javadoc code ({@code null} means the default:
 99    * javaCommand's boot class path)
 100    */
 101  2672 public DefaultJavadocModel(GlobalModel model, File javaCommand, Iterable<File> toolsPath) {
 102  2672 _model = model;
 103  2672 JVMBuilder builder = JVMBuilder.DEFAULT;
 104  2512 if (javaCommand != null) { builder = builder.javaCommand(javaCommand); }
 105  2671 if (toolsPath != null) { builder = builder.classPath(toolsPath); }
 106  2672 _jvmBuilder = builder;
 107  2672 _javadocErrorModel = new CompilerErrorModel();
 108    }
 109   
 110  704 public boolean isAvailable() { return true; }
 111   
 112    //-------------------------- Listener Management --------------------------//
 113   
 114    /** Add a JavadocListener to the model.
 115    * @param listener a listener that reacts to Javadoc events
 116    */
 117  158 public void addListener(JavadocListener listener) { _notifier.addListener(listener); }
 118   
 119    /** Remove a JavadocListener from the model. If the listener is not installed, this method has no effect.
 120    * @param listener a listener that reacts to Javadoc events
 121    */
 122  0 public void removeListener(JavadocListener listener) { _notifier.removeListener(listener); }
 123   
 124    /** Removes all JavadocListeners from this model. */
 125  0 public void removeAllListeners() { _notifier.removeAllListeners(); }
 126   
 127    //----------------------------- Error Results -----------------------------//
 128   
 129    /** Accessor for the Javadoc error model.
 130    * @return the CompilerErrorModel managing Javadoc errors.
 131    */
 132  42 public CompilerErrorModel getJavadocErrorModel() { return _javadocErrorModel; }
 133   
 134    /** Clears all current Javadoc errors. */
 135  4 public void resetJavadocErrors() {
 136  4 _javadocErrorModel = new CompilerErrorModel();
 137    }
 138   
 139    // -------------------- Javadoc All Documents --------------------
 140   
 141    /** Javadocs all open documents, after ensuring that all are saved. The user provides a destination, and the global
 142    * model provides the package info. Must run in the event-handling thread.
 143    * @param select a command object for selecting a directory and warning a user about bad input
 144    * @param saver a command object for saving a document (if it moved/changed)
 145    * @throws IOException if there is a problem manipulating files
 146    */
 147  0 public void javadocAll(DirectorySelector select, final FileSaveSelector saver) throws IOException {
 148   
 149    /* Only javadoc if all are saved. Removed because it is already done inside suggestJavadocDestination; fixes bug
 150    where pop-up is shown twice) */
 151  0 if (_model.hasModifiedDocuments() || _model.hasUntitledDocuments()) { return; } /* abort if files remain unsaved */
 152   
 153  0 Configuration config = DrJava.getConfig();
 154  0 File destDir = config.getSetting(OptionConstants.JAVADOC_DESTINATION);
 155   
 156    // Get the destination directory via the DirectorySelector, if appropriate.
 157  0 try {
 158  0 if (destDir.equals(FileOps.NULL_FILE)) {
 159    /* This is the default, stock behavior of a new install. If no destination is set, don't pass
 160    anything to the ui command. Let the command object decide what to do. */
 161  0 destDir = select.getDirectory(null);
 162    }
 163    else
 164    // Otherwise, tell the command object to prefer the config's default.
 165  0 destDir = select.getDirectory(destDir);
 166   
 167    // Make sure the destination is usable.
 168  0 while (!destDir.exists() || !destDir.isDirectory() || !destDir.canWrite()) {
 169  0 if (!destDir.getPath().equals("") && !destDir.exists()) {
 170    // If the choice doesn't exist, ask to create it.
 171  0 boolean create = select.askUser
 172    ("The directory you chose does not exist:\n'" + destDir + "'\nWould you like to create it?",
 173    "Create Directory?");
 174  0 if (create) {
 175  0 boolean dirMade = destDir.mkdirs();
 176  0 if (! dirMade) throw new IOException("Could not create directory: " + destDir);
 177    }
 178  0 else return;
 179    }
 180  0 else if (!destDir.isDirectory() || destDir.getPath().equals("")) {
 181    // We can't use it if it isn't a directory
 182  0 select.warnUser("The file you chose is not a directory:\n" +
 183    "'" + destDir + "'\n" +
 184    "Please choose another.",
 185    "Not a Directory!");
 186  0 destDir = select.getDirectory(null);
 187    }
 188    else {
 189    //If the directory isn't writable, tell the user and ask again.
 190  0 select.warnUser("The directory you chose is not writable:\n" +
 191    "'" + destDir + "'\n" +
 192    "Please choose another directory.",
 193    "Cannot Write to Destination!");
 194  0 destDir = select.getDirectory(null);
 195    }
 196    }
 197    }
 198  0 catch (OperationCanceledException oce) { return; } // If the user cancels anywhere, silently return.
 199   
 200  0 _notifier.javadocStarted(); // fire first so _javadocAllWorker can fire javadocEnded
 201    // Start a new thread to do the work.
 202  0 final File destDirF = destDir;
 203  0 new Thread("DrJava Javadoc Thread") {
 204  0 public void run() { _javadocAllWorker(destDirF, saver); }
 205    }.start();
 206    }
 207   
 208    /** This method handles most of the logic of performing a Javadoc operation, once we know that it won't be canceled.
 209    * @param destDirFile the destination directory for the doc files
 210    * @param saver a command object for saving a document (if it moved/changed)
 211    */
 212  0 private void _javadocAllWorker(final File destDirFile, FileSaveSelector saver) {
 213    // Note: JAVADOC_FROM_ROOTS is intended to set the -subpackages flag, but I don't think that's something
 214    // we should support -- in general, we only support performing operations on the files that are open.
 215    // (dlsmith r4189)
 216   
 217  0 final List<String> docFiles = new ArrayList<String>(); // files to send to Javadoc
 218   
 219  0 final List<OpenDefinitionsDocument> llDocs = new ArrayList<OpenDefinitionsDocument>();
 220  0 for (OpenDefinitionsDocument doc: _model.getOpenDefinitionsDocuments()) {
 221  0 try {
 222    // This will throw an IllegalStateException if no file can be found
 223  0 File docFile = _getFileFromDocument(doc, saver);
 224  0 final File file = IOUtil.attemptCanonicalFile(docFile);
 225   
 226    // If this is a language level file, make sure it has been compiled
 227  0 if (DrJavaFileUtils.isLLFile(file)) {
 228    // Utilities.showDebug("isLLFile=true: "+file);
 229  0 llDocs.add(doc);
 230  0 docFiles.add(DrJavaFileUtils.getJavaForLLFile(file).getPath());
 231    }
 232    else {
 233  0 docFiles.add(file.getPath());
 234    }
 235    }
 236    catch (IllegalStateException e) {
 237    // Something wrong with _getFileFromDocument; ignore
 238    }
 239    catch (IOException ioe) {
 240    // can't access file; ignore
 241    }
 242    }
 243   
 244    // Don't attempt to create Javadoc if no files are open, or if open file is unnamed.
 245  0 if (docFiles.size() == 0) {
 246    // Use EventQueue.invokeLater so that notification is deferred when running in the event thread.
 247  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.javadocEnded(false, destDirFile, true); } });
 248  0 return;
 249    }
 250   
 251  0 Utilities.invokeLater(new Runnable() { public void run() {
 252  0 if (_model.hasOutOfSyncDocuments(llDocs)) {
 253    // Utilities.showDebug("out of date");
 254  0 CompilerListener javadocAfterCompile = new DummyCompilerListener() {
 255  0 @Override public void compileAborted(Exception e) {
 256    // gets called if there are modified files and the user chooses NOT to save the files
 257    // see bug report 2582488: Hangs If Testing Modified File, But Choose "No" for Saving
 258  0 final CompilerListener listenerThis = this;
 259    // Utilities.showDebug("compile aborted");
 260    // always remove this listener after its first execution
 261  0 EventQueue.invokeLater(new Runnable() {
 262  0 public void run() {
 263  0 _model.getCompilerModel().removeListener(listenerThis);
 264    // fail Javadoc
 265  0 _notifier.javadocEnded(false, destDirFile, true);
 266    }
 267    });
 268    }
 269  0 @Override public void compileEnded(File workDir, List<? extends File> excludedFiles) {
 270  0 final CompilerListener listenerThis = this;
 271  0 try {
 272    // Utilities.showDebug("compile ended");
 273  0 if (_model.hasOutOfSyncDocuments(llDocs) || _model.getNumCompErrors() > 0) {
 274    // Utilities.showDebug("still out of date, fail");
 275    // fail Javadoc
 276  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.javadocEnded(false, destDirFile, true); } });
 277  0 return;
 278    }
 279  0 EventQueue.invokeLater(new Runnable() { // defer running this code; would prefer to waitForInterpreter
 280  0 public void run() {
 281    // Utilities.showDebug("running Javadoc");
 282    // Run the actual Javadoc process
 283  0 _runJavadoc(docFiles, destDirFile, IterUtil.<String>empty(), true);
 284    }
 285    });
 286    }
 287    finally { // always remove this listener after its first execution
 288  0 EventQueue.invokeLater(new Runnable() {
 289  0 public void run() { _model.getCompilerModel().removeListener(listenerThis); }
 290    });
 291    }
 292    }
 293    };
 294   
 295  0 _notifyCompileBeforeJavadoc(javadocAfterCompile);
 296  0 return;
 297    }
 298   
 299    // Run the actual Javadoc process
 300  0 _runJavadoc(docFiles, destDirFile, IterUtil.<String>empty(), true);
 301    } });
 302    }
 303   
 304   
 305   
 306    // -------------------- Javadoc Current Document --------------------
 307   
 308    /** Generates Javadoc for the given document only, after ensuring it is saved. Saves the output in a temp directory
 309    * which is passed to _javadocDocuemntWorker, which is passed to a subsequent javadocEnded event.
 310    * @param doc Document to generate Javadoc for
 311    * @param saver a command object for saving the document (if it moved/changed)
 312    * @throws IOException if there is a problem manipulating files
 313    */
 314  0 public void javadocDocument(final OpenDefinitionsDocument doc, final FileSaveSelector saver) throws IOException {
 315    // Prompt to save if necessary
 316    // (TO DO: should only need to save the current document)
 317  0 if (doc.isUntitled() || doc.isModifiedSinceSave()) _notifier.saveBeforeJavadoc();
 318   
 319    // Make sure it is saved
 320  0 if (doc.isUntitled() || doc.isModifiedSinceSave()) return; // The user didn't save, so don't generate Javadoc
 321   
 322    // Try to get the file from the document
 323  0 File docFile = _getFileFromDocument(doc, saver);
 324  0 final File file = IOUtil.attemptCanonicalFile(docFile);
 325  0 final File javaFile = DrJavaFileUtils.getJavaForLLFile(file);
 326   
 327  0 Utilities.invokeLater(new Runnable() { public void run() {
 328    // If this is a language level file, make sure it has been compiled
 329  0 if (DrJavaFileUtils.isLLFile(file)) {
 330    // Utilities.showDebug("isLLFile = true");
 331  0 final List<OpenDefinitionsDocument> singleton = new ArrayList<OpenDefinitionsDocument>();
 332  0 singleton.add(doc);
 333  0 if (_model.hasOutOfSyncDocuments(singleton)) {
 334    // Utilities.showDebug("out of date");
 335  0 CompilerListener javadocAfterCompile = new DummyCompilerListener() {
 336  0 @Override public void compileAborted(Exception e) {
 337    // gets called if there are modified files and the user chooses NOT to save the files
 338    // see bug report 2582488: Hangs If Testing Modified File, But Choose "No" for Saving
 339  0 final CompilerListener listenerThis = this;
 340    // always remove this listener after its first execution
 341  0 EventQueue.invokeLater(new Runnable() {
 342  0 public void run() {
 343  0 _model.getCompilerModel().removeListener(listenerThis);
 344    // fail Javadoc
 345  0 _notifier.javadocEnded(false, null, false);
 346    }
 347    });
 348    }
 349  0 @Override public void compileEnded(File workDir, List<? extends File> excludedFiles) {
 350  0 final CompilerListener listenerThis = this;
 351  0 try {
 352    // Utilities.showDebug("compile ended");
 353  0 if (_model.hasOutOfSyncDocuments(singleton) || _model.getNumCompErrors() > 0) {
 354    // Utilities.showDebug("still out of date, fail");
 355    // fail Javadoc
 356  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.javadocEnded(false, null, false); } });
 357  0 return;
 358    }
 359  0 EventQueue.invokeLater(new Runnable() { // defer running this code; would prefer to waitForInterpreter
 360  0 public void run() {
 361  0 try {
 362    // Utilities.showDebug("running Javadoc");
 363  0 _rawJavadocDocument(javaFile);
 364    }
 365    catch(IOException ioe) {
 366    // fail Javadoc
 367  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.javadocEnded(false, null, false); } });
 368    }
 369    }
 370    });
 371    }
 372    finally { // always remove this listener after its first execution
 373  0 EventQueue.invokeLater(new Runnable() {
 374  0 public void run() { _model.getCompilerModel().removeListener(listenerThis); }
 375    });
 376    }
 377    }
 378    };
 379   
 380  0 _notifyCompileBeforeJavadoc(javadocAfterCompile);
 381  0 return;
 382    }
 383    }
 384  0 try {
 385  0 _rawJavadocDocument(javaFile);
 386    }
 387    catch(IOException ioe) {
 388    // fail Javadoc
 389  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.javadocEnded(false, null, false); } });
 390    }
 391    } });
 392    }
 393   
 394  0 private void _rawJavadocDocument(final File file) throws IOException {
 395    // Generate to a temporary directory
 396  0 final File destDir = IOUtil.createAndMarkTempDirectory("DrJava-javadoc", "");
 397   
 398  0 _notifier.javadocStarted(); // fire first so _javadocDocumntWorker can fire javadocEnded
 399    // Start a new thread to do the work.
 400  0 new Thread("DrJava Javadoc Thread") {
 401  0 public void run() {
 402  0 Iterable<String> extraArgs = IterUtil.make("-noindex", "-notree", "-nohelp", "-nonavbar");
 403  0 _runJavadoc(IterUtil.make(file.getPath()), destDir, extraArgs, false);
 404    }
 405    }.start();
 406    }
 407   
 408    // -------------------- Helper Methods --------------------
 409   
 410    /** Helper method to notify JavadocModel listeners that all open files must be compiled before Javadoc is run. */
 411  0 private void _notifyCompileBeforeJavadoc(final CompilerListener afterCompile) {
 412  0 Utilities.invokeLater(new Runnable() {
 413  0 public void run() { _notifier.compileBeforeJavadoc(afterCompile); }
 414    });
 415    }
 416   
 417    /** Suggests a default location for generating Javadoc, based on the given document's source root. (Appends
 418    * JavadocModel.SUGGESTED_DIR_NAME to the sourceroot.) Ensures that the document is saved first, or else no
 419    * reasonable suggestion will be found.
 420    * @param doc Document with the source root to use as the default.
 421    * @return Suggested destination directory, or null if none could be determined.
 422    */
 423  3 public File suggestJavadocDestination(OpenDefinitionsDocument doc) {
 424  3 _attemptSaveAllDocuments();
 425   
 426  3 try {
 427  3 File sourceRoot = doc.getSourceRoot();
 428  2 return new File(sourceRoot, SUGGESTED_DIR_NAME);
 429    }
 430  1 catch (InvalidPackageException ipe) { return null; }
 431    }
 432   
 433    /** If any documents are modified, this gives the user a chance
 434    * to save them before proceeding.
 435    *
 436    * Callers can check _getter.hasModifiedDocuments() after calling
 437    * this method to determine if the user cancelled the save process.
 438    */
 439  3 private void _attemptSaveAllDocuments() {
 440    // Only javadoc if all are saved.
 441  1 if (_model.hasModifiedDocuments() || _model.hasUntitledDocuments()) _notifier.saveBeforeJavadoc();
 442    }
 443   
 444    /** Run a new process to generate javdocs, and then tell the listeners when we're done.
 445    *
 446    * @param files List of files to generate
 447    * @param destDir Directory where the results are being saved
 448    * @param extraArgs List of additional arguments to use with javadoc (besides those gathered from config settings)
 449    * @param allDocs Whether this is running on all documents. If Javadoc is not run on all documents, the target directory will be deleted when DrJava exits
 450    */
 451  0 private void _runJavadoc(Iterable<String> files, final File destDir, Iterable<String> extraArgs, final boolean allDocs) {
 452  0 Iterable<String> args = IterUtil.empty();
 453  0 args = IterUtil.compose(args, IterUtil.make("-d", destDir.getPath()));
 454  0 args = IterUtil.compose(args, IterUtil.make("-classpath", IOUtil.pathToString(_model.getClassPath())));
 455  0 args = IterUtil.compose(args, _getLinkArgs());
 456  0 args = IterUtil.compose(args, "-" + DrJava.getConfig().getSetting(OptionConstants.JAVADOC_ACCESS_LEVEL));
 457  0 args = IterUtil.compose(args, extraArgs);
 458  0 String custom = DrJava.getConfig().getSetting(OptionConstants.JAVADOC_CUSTOM_PARAMS);
 459  0 args = IterUtil.compose(args, ArgumentTokenizer.tokenize(custom));
 460  0 args = IterUtil.compose(args, files);
 461   
 462  0 List<DJError> errors = new ArrayList<DJError>();
 463  0 try {
 464  0 Process p = _jvmBuilder.start("com.sun.tools.javadoc.Main", args);
 465  0 Thunk<String> outputString = ConcurrentUtil.processOutAsString(p);
 466  0 Thunk<String> errorString = ConcurrentUtil.processErrAsString(p);
 467  0 p.waitFor();
 468  0 errors.addAll(_extractErrors(outputString.value()));
 469  0 errors.addAll(_extractErrors(errorString.value()));
 470    }
 471    catch (IOException e) {
 472  0 errors.add(new DJError("IOException: " + e.getMessage(), false));
 473    }
 474    catch (InterruptedException e) {
 475  0 errors.add(new DJError("InterruptedException: " + e.getMessage(), false));
 476    }
 477   
 478  0 _javadocErrorModel = new CompilerErrorModel(IterUtil.toArray(errors, DJError.class), _model);
 479   
 480    // waitFor() exit value is 1 for both errors and warnings, so it's no use
 481  0 final boolean success = _javadocErrorModel.hasOnlyWarnings();
 482  0 if (!allDocs) { IOUtil.deleteOnExitRecursively(destDir); }
 483    // Use EventQueue.invokeLater so that notification is deferred when running in the event thread.
 484  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.javadocEnded(success, destDir, allDocs); } });
 485    }
 486   
 487  0 private Iterable<String> _getLinkArgs() {
 488  0 Configuration config = DrJava.getConfig();
 489  0 String linkVersion = config.getSetting(OptionConstants.JAVADOC_LINK_VERSION);
 490  0 if (linkVersion.equals(OptionConstants.JAVADOC_1_3_TEXT)) {
 491  0 return IterUtil.make("-link", config.getSetting(OptionConstants.JAVADOC_1_3_LINK));
 492    }
 493  0 else if (linkVersion.equals(OptionConstants.JAVADOC_1_4_TEXT)) {
 494  0 return IterUtil.make("-link", config.getSetting(OptionConstants.JAVADOC_1_4_LINK));
 495    }
 496  0 else if (linkVersion.equals(OptionConstants.JAVADOC_1_5_TEXT)) {
 497  0 return IterUtil.make("-link", config.getSetting(OptionConstants.JAVADOC_1_5_LINK));
 498    }
 499  0 else if (linkVersion.equals(OptionConstants.JAVADOC_1_6_TEXT)) {
 500  0 return IterUtil.make("-link", config.getSetting(OptionConstants.JAVADOC_1_6_LINK));
 501    }
 502    else {
 503    // should never happen -- use an enum to guarantee
 504  0 return IterUtil.empty();
 505    }
 506    }
 507   
 508    /** Reads through javadoc output text, looking for Javadoc errors. This code will detect Exceptions and
 509    * Errors thrown during generation of the output, as well as errors and warnings generated by Javadoc.
 510    * This code works for both JDK 1.3 and 1.4, assuming you pass in data from the correct stream. (Be safe
 511    * and check both.)
 512    */
 513  0 private List<DJError> _extractErrors(String text) {
 514  0 BufferedReader r = new BufferedReader(new StringReader(text));
 515  0 List<DJError> result = new ArrayList<DJError>();
 516   
 517  0 String[] errorIndicators = new String[]{ "Error: ", "Exception: ", "invalid flag:" };
 518   
 519  0 try {
 520  0 String output = r.readLine();
 521  0 while (output != null) {
 522  0 if (TextUtil.containsAny(output, errorIndicators)) {
 523    // If we found one, put the remaining stream contents in one DJError.
 524  0 result.add(new DJError(output + '\n' + IOUtil.toString(r), false));
 525    }
 526    else {
 527    // Otherwise, parser for a normal error message.
 528  0 DJError error = _parseJavadocErrorLine(output);
 529  0 if (error != null) { result.add(error); }
 530    }
 531  0 output = r.readLine();
 532    }
 533    }
 534  0 catch (IOException e) { error.log(e); /* should not happen, since we're reading from a string */ }
 535   
 536  0 return result;
 537    }
 538   
 539    /** Convert a line of Javadoc text to a DJError. If unable to do so, returns {@code null}. */
 540  0 private DJError _parseJavadocErrorLine(String line) {
 541  0 int errStart = line.indexOf(".java:");
 542  0 if (errStart == -1) { return null; /* ignore the line if it doesn't have file info */ }
 543    // filename is everything up to and including the '.java'
 544  0 String fileName = line.substring(0, errStart+5);
 545   
 546    // line number is all contiguous number characters after the colon
 547  0 int lineno = -1;
 548  0 final StringBuilder linenoString = new StringBuilder();
 549  0 int pos = errStart+6;
 550  0 while ((line.charAt(pos) >= '0') && (line.charAt(pos) <= '9')) {
 551  0 linenoString.append(line.charAt(pos));
 552  0 pos++;
 553    }
 554    // Hopefully, there is a colon after the line number but before the error message.
 555    // If so, record the line number.
 556    // Otherwise, try to recover by just using everything after ERR_INDICATOR as the error message
 557  0 if (line.charAt(pos) == ':') {
 558  0 try {
 559    // Adjust Javadoc's one-based line numbers to our zero-based indeces.
 560  0 lineno = Integer.valueOf(linenoString.toString()).intValue() -1;
 561    } catch (NumberFormatException e) {
 562    }
 563    } else {
 564  0 pos = errStart;
 565    }
 566   
 567    // error message is everything after the colon and space that are after the line number
 568  0 String errMessage = line.substring(pos+2);
 569   
 570    // check to see if the first word in the error message is "warning"
 571  0 boolean isWarning = false;
 572  0 if (errMessage.substring(0, 7).equalsIgnoreCase("warning")) {
 573  0 isWarning = true;
 574    }
 575   
 576  0 if (lineno >= 0) { return new DJError(new File(fileName), lineno, 0, errMessage, isWarning); }
 577  0 else { return new DJError(new File(fileName), errMessage, isWarning); }
 578    }
 579   
 580   
 581   
 582    /** Attempts to get the file from the given document.
 583    * If the file has moved, we use the given FileSaveSelector to let the user save it
 584    * in a new location.
 585    *
 586    * @param doc OpenDefinitionsDocument from which to get the file
 587    * @param saver FileSaveSelector to allow the user to save the file if it has moved.
 588    *
 589    * @throws IllegalStateException if the doc has no file (hasn't been saved)
 590    * @throws IOException if the file can't be saved after it was moved
 591    */
 592  0 private File _getFileFromDocument(OpenDefinitionsDocument doc, FileSaveSelector saver) throws IOException {
 593  0 try {
 594    // This call will abort the iteration if there is no file, unless we can recover (like for a FileMovedException).
 595  0 return doc.getFile();
 596    }
 597    catch (FileMovedException fme) {
 598    // The file has moved - prompt the user to recover.
 599    // XXX: This is probably not thread safe!
 600  0 if (saver.shouldSaveAfterFileMoved(doc, fme.getFile())) {
 601  0 try {
 602  0 doc.saveFileAs(saver);
 603  0 return doc.getFile();
 604    }
 605    catch (FileMovedException fme2) {
 606    // If the user is this intent on shooting themselves in the foot,
 607    // get out of the way.
 608  0 fme2.printStackTrace();
 609  0 throw new IOException("Could not find file: " + fme2);
 610    }
 611    }
 612    else {
 613  0 throw new IllegalStateException("No file exists for this document.");
 614    }
 615    }
 616    }
 617   
 618    }