Clover coverage report - DrJava Test Coverage (drjava-20110828-r5448)
Coverage timestamp: Sun Aug 28 2011 03:13:33 CDT
file stats: LOC: 4,304   Methods: 494
NCLOC: 2,517   Classes: 5
 
 Source file Conditionals Statements Methods TOTAL
AbstractGlobalModel.java 50.2% 61.3% 57.5% 58.6%
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    package edu.rice.cs.drjava.model;
 37   
 38    import java.awt.Color;
 39    import java.awt.Container;
 40    import java.awt.EventQueue;
 41    import java.awt.Font;
 42    import java.awt.event.FocusEvent;
 43    import java.awt.event.FocusListener;
 44    import java.awt.print.PageFormat;
 45    import java.awt.print.Pageable;
 46    import java.awt.print.PrinterException;
 47    import java.awt.print.PrinterJob;
 48    import java.awt.Component;
 49    import java.io.File;
 50    import java.io.FileNotFoundException;
 51    import java.io.FileReader;
 52    import java.io.FilenameFilter;
 53    import java.io.IOException;
 54    import java.io.OutputStream;
 55    import java.io.StringReader;
 56   
 57    import java.util.AbstractMap;
 58    import java.util.ArrayList;
 59    import java.util.Collection;
 60    import java.util.Collections;
 61    import java.util.Comparator;
 62    import java.util.Set;
 63    import java.util.LinkedHashSet;
 64    import java.util.HashMap;
 65    import java.util.HashSet;
 66    import java.util.LinkedHashMap;
 67    import java.util.LinkedList;
 68    import java.util.List;
 69    import java.util.Vector;
 70    import java.util.WeakHashMap;
 71    import java.util.Map;
 72   
 73    import javax.swing.*;
 74    import javax.swing.event.DocumentListener;
 75    import javax.swing.event.UndoableEditListener;
 76    import javax.swing.text.AttributeSet;
 77    import javax.swing.text.BadLocationException;
 78    import javax.swing.text.DefaultEditorKit;
 79    import javax.swing.text.Element;
 80    import javax.swing.text.Position;
 81    import javax.swing.text.Segment;
 82    import javax.swing.text.Style;
 83    import javax.swing.ProgressMonitor;
 84   
 85    import edu.rice.cs.drjava.DrJava;
 86    import edu.rice.cs.drjava.DrJavaRoot;
 87    import edu.rice.cs.drjava.config.Option;
 88    import edu.rice.cs.drjava.config.OptionParser;
 89    import edu.rice.cs.drjava.config.OptionConstants;
 90    import edu.rice.cs.drjava.config.OptionEvent;
 91    import edu.rice.cs.drjava.config.OptionListener;
 92    import edu.rice.cs.drjava.model.cache.DCacheAdapter;
 93    import edu.rice.cs.drjava.model.cache.DDReconstructor;
 94    import edu.rice.cs.drjava.model.cache.DocumentCache ;
 95    import edu.rice.cs.drjava.model.compiler.CompilerModel;
 96    import edu.rice.cs.drjava.model.debug.Breakpoint;
 97    import edu.rice.cs.drjava.model.debug.DebugBreakpointData;
 98    import edu.rice.cs.drjava.model.debug.DebugException ;
 99    import edu.rice.cs.drjava.model.debug.DebugWatchData;
 100    import edu.rice.cs.drjava.model.debug.Debugger;
 101    import edu.rice.cs.drjava.model.debug.NoDebuggerAvailable;
 102    import edu.rice.cs.drjava.model.javadoc.JavadocModel;
 103    import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException;
 104    import edu.rice.cs.drjava.model.definitions.CompoundUndoManager;
 105    import edu.rice.cs.drjava.model.definitions.DefinitionsDocument;
 106    import edu.rice.cs.drjava.model.definitions.DefinitionsEditorKit;
 107    import edu.rice.cs.drjava.model.definitions.DocumentUIListener ;
 108    import edu.rice.cs.drjava.model.definitions.InvalidPackageException;
 109    import edu.rice.cs.drjava.model.definitions.indent.Indenter;
 110    import edu.rice.cs.drjava.model.definitions.reducedmodel.HighlightStatus;
 111    import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelControl;
 112    import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelState;
 113    import edu.rice.cs.drjava.model.junit.JUnitModel;
 114    import edu.rice.cs.drjava.model.print.DrJavaBook;
 115    import edu.rice.cs.drjava.model.repl.DefaultInteractionsModel ;
 116    import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
 117    import edu.rice.cs.drjava.model.repl.InteractionsDocument;
 118    import edu.rice.cs.drjava.model.repl.InteractionsScriptModel;
 119    import edu.rice.cs.drjava.project.DocFile ;
 120    import edu.rice.cs.drjava.project.DocumentInfoGetter;
 121    import edu.rice.cs.drjava.project.MalformedProjectFileException;
 122    import edu.rice.cs.drjava.project.ProjectFileIR;
 123    import edu.rice.cs.drjava.project.ProjectFileParserFacade;
 124    import edu.rice.cs.drjava.project.ProjectProfile;
 125    import edu.rice.cs.drjava.ui.DrJavaErrorHandler;
 126   
 127    import edu.rice.cs.plt.reflect.ReflectUtil;
 128    import edu.rice.cs.plt.tuple.Pair;
 129    import edu.rice.cs.plt.io.IOUtil;
 130    import edu.rice.cs.plt.iter.IterUtil;
 131    import edu.rice.cs.plt.collect.CollectUtil;
 132    import edu.rice.cs.plt.lambda.LambdaUtil;
 133    import edu.rice.cs.plt.lambda.Predicate;
 134   
 135    import edu.rice.cs.util.FileOpenSelector;
 136    import edu.rice.cs.util.FileOps;
 137    import edu.rice.cs.util.Log;
 138    import edu.rice.cs.util.NullFile;
 139    import edu.rice.cs.util.AbsRelFile;
 140    import edu.rice.cs.util.OperationCanceledException;
 141    import edu.rice.cs.util.StringOps;
 142    import edu.rice.cs.util.UnexpectedException;
 143    import edu.rice.cs.util.docnavigation.AWTContainerNavigatorFactory;
 144    import edu.rice.cs.util.docnavigation.IDocumentNavigator;
 145    import edu.rice.cs.util.docnavigation.INavigationListener;
 146    import edu.rice.cs.util.docnavigation.INavigatorItem;
 147    import edu.rice.cs.util.docnavigation.INavigatorItemFilter;
 148    import edu.rice.cs.util.docnavigation.JTreeSortNavigator;
 149    import edu.rice.cs.util.docnavigation.NodeData;
 150    import edu.rice.cs.util.docnavigation.NodeDataVisitor;
 151    import edu.rice.cs.util.swing.AsyncCompletionArgs ;
 152    import edu.rice.cs.util.swing.AsyncTask;
 153    import edu.rice.cs.util.swing.IAsyncProgress;
 154    import edu.rice.cs.util.swing.DocumentIterator;
 155    import edu.rice.cs.util.swing.Utilities;
 156    import edu.rice.cs.util.text.AbstractDocumentInterface;
 157    import edu.rice.cs.util.text.ConsoleDocument;
 158   
 159    import static edu.rice.cs.plt.debug.DebugUtil.debug;
 160   
 161    /** In simple terms, a DefaultGlobalModel without an interpreter, compiler, junit testing, debugger or javadoc.
 162    * Hence, it only has only document handling functionality
 163    * @version $Id: AbstractGlobalModel.java 5445 2011-08-17 20:32:57Z rcartwright $
 164    */
 165    public class AbstractGlobalModel implements SingleDisplayModel, OptionConstants, DocumentIterator {
 166   
 167    public static final Log _log = new Log("GlobalModel.txt", false);
 168   
 169    /** A document cache that manages how many unmodified documents are open at once. */
 170    protected DocumentCache _cache;
 171   
 172    static final String DOCUMENT_OUT_OF_SYNC_MSG =
 173    "Current document is out of sync with the Interactions Pane and should be recompiled!\n";
 174   
 175    static final String CLASSPATH_OUT_OF_SYNC_MSG =
 176    "Interactions Pane is out of sync with the current classpath and should be reset!\n";
 177   
 178    // ----- FIELDS -----
 179   
 180    /** Keeps track of all listeners to the model, and has the ability to notify them of some event. Originally used
 181    * a Command Pattern style, but this has been replaced by having EventNotifier directly implement all listener
 182    * interfaces it supports. Set in constructor so that subclasses can install their own notifier with additional
 183    * methods.
 184    */
 185    public final GlobalEventNotifier _notifier = new GlobalEventNotifier();
 186   
 187    // ---- Definitions fields ----
 188   
 189    /** Factory for new definitions documents and views.*/
 190    protected final DefinitionsEditorKit _editorKit = new DefinitionsEditorKit(_notifier);
 191   
 192    /** Collection for storing all OpenDefinitionsDocuments. */
 193    private final AbstractMap<File, OpenDefinitionsDocument> _documentsRepos =
 194    new LinkedHashMap<File, OpenDefinitionsDocument>();
 195   
 196    // ---- Input/Output Document Fields ----
 197   
 198    /** The document used to display System.out and System.err, and to read from System.in. */
 199    protected final ConsoleDocument _consoleDoc;
 200   
 201    /** The document adapter used in the console document. */
 202    protected final InteractionsDJDocument _consoleDocAdapter;
 203   
 204    /** A PageFormat object for printing to paper. */
 205    protected volatile PageFormat _pageFormat = new PageFormat();
 206   
 207    /** The active document pointer, which will never be null once the constructor is done.
 208    * Maintained by the _gainVisitor with a navigation listener.
 209    */
 210    private volatile OpenDefinitionsDocument _activeDocument;
 211   
 212    /** A pointer to the active directory, which is not necessarily the parent of the active document
 213    * The user may click on a folder component in the navigation pane and that will set this field without
 214    * setting the active document. It is used by the newFile method to place new files into the active directory.
 215    */
 216    private volatile File _activeDirectory;
 217   
 218    /** A state varible indicating whether the class path has changed. Reset to false by resetInteractions. */
 219    private volatile boolean classPathChanged = false;
 220   
 221    /** The abstract container which contains views of open documents and allows user to navigate document focus among
 222    * this collection of open documents
 223    */
 224    protected volatile IDocumentNavigator<OpenDefinitionsDocument> _documentNavigator =
 225    new AWTContainerNavigatorFactory<OpenDefinitionsDocument>().makeListNavigator();
 226   
 227    /** Notifier list for the global model. */
 228  0 public GlobalEventNotifier getNotifier() { return _notifier; }
 229   
 230    /** Manager for breakpoint regions. */
 231    protected final ConcreteRegionManager<Breakpoint> _breakpointManager;
 232   
 233    /** @return manager for breakpoint regions. */
 234  466 public RegionManager<Breakpoint> getBreakpointManager() { return _breakpointManager; }
 235   
 236    /** Manager for bookmark regions. */
 237    protected final ConcreteRegionManager<MovingDocumentRegion> _bookmarkManager;
 238   
 239    /** @return manager for bookmark regions. */
 240  242 public RegionManager<MovingDocumentRegion> getBookmarkManager() { return _bookmarkManager; }
 241   
 242    /** Managers for find result regions. */
 243    protected final LinkedList<RegionManager<MovingDocumentRegion>> _findResultsManagers;
 244   
 245    /** @return new copy of list of find results managers for find result regions. */
 246  79 public List<RegionManager<MovingDocumentRegion>> getFindResultsManagers() {
 247  79 return new LinkedList<RegionManager<MovingDocumentRegion>>(_findResultsManagers);
 248    }
 249   
 250    /** @return new manager for find result regions. */
 251  0 public RegionManager<MovingDocumentRegion> createFindResultsManager() {
 252  0 ConcreteRegionManager<MovingDocumentRegion> rm = new ConcreteRegionManager<MovingDocumentRegion>();
 253  0 _findResultsManagers.add(rm);
 254   
 255  0 return rm;
 256    }
 257   
 258    /** Remove a manager from the model. */
 259  0 public void removeFindResultsManager(RegionManager<MovingDocumentRegion> rm) {
 260  0 _findResultsManagers.remove(rm);
 261    }
 262   
 263    /** Manager for browser history regions. */
 264    protected final BrowserHistoryManager _browserHistoryManager;
 265   
 266    /** @return manager for browser history regions. */
 267  178 public BrowserHistoryManager getBrowserHistoryManager() { return _browserHistoryManager; }
 268   
 269    // /** Completion monitor for loading the files of a project (as OpenDefinitionsDocuments). */
 270    // public final CompletionMonitor projectLoading = new CompletionMonitor();
 271   
 272    // Lightweight parsing is disabled until we have something that is beneficial and works better in the background.
 273    // /** Light-weight parsing controller. */
 274    // protected LightWeightParsingControl _parsingControl;
 275    //
 276    // /** @return the parsing control */
 277    // public LightWeightParsingControl getParsingControl() { return _parsingControl; }
 278   
 279    // ----- CONSTRUCTORS -----
 280   
 281    /** Constructs a new GlobalModel. */
 282  159 public AbstractGlobalModel() {
 283  159 _cache = new DocumentCache();
 284   
 285  159 _consoleDocAdapter = new InteractionsDJDocument(_notifier);
 286  159 _consoleDoc = new ConsoleDocument(_consoleDocAdapter);
 287   
 288  159 _bookmarkManager = new ConcreteRegionManager<MovingDocumentRegion>();
 289  159 _findResultsManagers = new LinkedList<RegionManager<MovingDocumentRegion>>();
 290  159 _browserHistoryManager = new BrowserHistoryManager();
 291   
 292  159 _breakpointManager = new ConcreteRegionManager<Breakpoint>();
 293    /* The following method was included in an anonymous class definition of _breakpointManager, but it
 294    was inacessible because no such method exists in the visible interface of ConcreteRegionManager. */
 295   
 296    // public boolean changeRegionHelper(final Breakpoint oldBP, final Breakpoint newBP) {
 297    // // override helper so the enabled flag is copied
 298    // if (oldBP.isEnabled() != newBP.isEnabled()) {
 299    // oldBP.setEnabled(newBP.isEnabled());
 300    // return true;
 301    // }
 302    // return false;
 303    // }
 304    // };
 305  159 _registerOptionListeners();
 306   
 307  159 setFileGroupingState(makeFlatFileGroupingState());
 308  159 Utilities.invokeLater(new Runnable() { public void run() { _notifier.projectRunnableChanged(); } });
 309  159 _init();
 310    }
 311   
 312  159 private void _init() {
 313   
 314    /** This visitor is invoked by the DocumentNavigator to update _activeDocument among other things */
 315  159 final NodeDataVisitor<OpenDefinitionsDocument, Boolean> _gainVisitor =
 316    new NodeDataVisitor<OpenDefinitionsDocument, Boolean>() {
 317  396 public Boolean itemCase(OpenDefinitionsDocument doc, Object... p) {
 318  396 _setActiveDoc(doc); // sets _activeDocument, the shadow copy of the active document
 319    // addToBrowserHistory();
 320   
 321    // Utilities.showDebug("Setting the active doc done");
 322  396 final File oldDir = _activeDirectory; // _activeDirectory can be null
 323  396 final File dir = doc.getParentDirectory(); // dir can be null
 324  396 if (dir != null && ! dir.equals(oldDir)) {
 325    /* If the file is in External or Auxiliary Files then then we do not want to change our project directory
 326    * to something outside the project. ?? */
 327  189 _activeDirectory = dir;
 328  189 _notifier.currentDirectoryChanged(_activeDirectory);
 329    }
 330  396 return Boolean.valueOf(true);
 331    }
 332  0 public Boolean fileCase(File f, Object... p) {
 333  0 if (! f.isAbsolute()) { // should never happen because all file names are canonicalized
 334  0 File root = _state.getProjectFile().getParentFile().getAbsoluteFile();
 335  0 f = new File(root, f.getPath());
 336    }
 337  0 _activeDirectory = f; // Invariant: activeDirectory != null
 338  0 _notifier.currentDirectoryChanged(f);
 339  0 return Boolean.valueOf(true);
 340    }
 341  0 public Boolean stringCase(String s, Object... p) { return Boolean.valueOf(false); }
 342    };
 343   
 344    /** Listener that invokes the _gainVisitor when a selection is made in the document navigator. */
 345  159 _documentNavigator.addNavigationListener(new INavigationListener<OpenDefinitionsDocument>() {
 346  396 public void gainedSelection(NodeData<? extends OpenDefinitionsDocument> dat, boolean modelInitiated) {
 347  396 dat.execute(_gainVisitor, modelInitiated); }
 348  291 public void lostSelection(NodeData<? extends OpenDefinitionsDocument> dat, boolean modelInitiated) {
 349    /* not important, only one document selected at a time */ }
 350    });
 351   
 352    // The document navigator gets the focus in
 353  159 _documentNavigator.addFocusListener(new FocusListener() {
 354  0 public void focusGained(FocusEvent e) {
 355    // System.err.println("_documentNavigator.focusGained(...) called");
 356    // if (_documentNavigator.getCurrent() != null) // past selection is leaf node
 357  0 Utilities.invokeLater(new Runnable() { public void run() { _notifier.focusOnDefinitionsPane(); } });
 358    }
 359  0 public void focusLost(FocusEvent e) { }
 360    });
 361   
 362  159 _ensureNotEmpty();
 363  159 setActiveFirstDocument();
 364   
 365    // setup option listener for clipboard history
 366  159 OptionListener<Integer> clipboardHistorySizeListener = new OptionListener<Integer>() {
 367  0 public void optionChanged(OptionEvent<Integer> oce) {
 368  0 ClipboardHistoryModel.singleton().resize(oce.value);
 369    }
 370    };
 371  159 DrJava.getConfig().addOptionListener(CLIPBOARD_HISTORY_SIZE, clipboardHistorySizeListener);
 372  159 ClipboardHistoryModel.singleton().resize(DrJava.getConfig().getSetting(CLIPBOARD_HISTORY_SIZE).intValue());
 373   
 374    // setup option listener for browser history
 375  159 OptionListener<Integer> browserHistoryMaxSizeListener = new OptionListener<Integer>() {
 376  0 public void optionChanged(OptionEvent<Integer> oce) {
 377  0 AbstractGlobalModel.this.getBrowserHistoryManager().setMaximumSize(oce.value);
 378    }
 379    };
 380  159 DrJava.getConfig().addOptionListener(BROWSER_HISTORY_MAX_SIZE, browserHistoryMaxSizeListener);
 381  159 getBrowserHistoryManager().setMaximumSize(DrJava.getConfig().getSetting(BROWSER_HISTORY_MAX_SIZE).intValue());
 382    }
 383   
 384    // ----- STATE -----
 385   
 386    /** Specifies the state of the navigator pane. The global model delegates the compileAll command to the _state.
 387    * FileGroupingState synchronization is handled by the compilerModel (??).
 388    */
 389    protected volatile FileGroupingState _state;
 390   
 391    /** @param state the new file grouping state. */
 392  165 public void setFileGroupingState(FileGroupingState state) {
 393  165 _state = state;
 394  165 _notifier.projectRunnableChanged();
 395  165 _notifier.projectBuildDirChanged();
 396  165 _notifier.projectWorkDirChanged();
 397   
 398    // _notifier.projectModified(); // not currently used
 399    }
 400   
 401    /** Adds a document to the list of auxiliary files within _state. The LinkedList class is not thread safe, so
 402    * the add operation is synchronized.
 403    */
 404  1 public void addAuxiliaryFile(OpenDefinitionsDocument doc) { _state.addAuxFile(doc.getRawFile()); }
 405   
 406    /** Removes a document from the list of auxiliary files within _state. The LinkedList class is not thread safe, so
 407    * operations on _auxiliaryFiles are synchronized.
 408    */
 409  1 public void removeAuxiliaryFile(OpenDefinitionsDocument doc) { _state.remAuxFile(doc.getRawFile()); }
 410   
 411  4 protected FileGroupingState
 412    makeProjectFileGroupingState(File pr, String main, File bd, File wd, File project, File[] srcFiles, File[] auxFiles,
 413    File[] excludedFiles, Iterable<AbsRelFile> cp, File cjf, int cjflags, boolean refresh,
 414    String manifest, Map<OptionParser<?>,String> storedPreferences) {
 415   
 416  4 return new ProjectFileGroupingState(pr, main, bd, wd, project, srcFiles, auxFiles, excludedFiles, cp, cjf, cjflags,
 417    refresh, manifest, storedPreferences);
 418    }
 419   
 420    /** @return true if the class path state has been changed. */
 421  24 public boolean isClassPathChanged() { return classPathChanged; }
 422   
 423    /** Updates the classpath state. */
 424  438 public void setClassPathChanged(boolean changed) {
 425  438 classPathChanged = changed;
 426    }
 427   
 428    /** Notifies the project state that the project has been changed. */
 429  132 public void setProjectChanged(boolean changed) {
 430  132 _state.setProjectChanged(changed);
 431    // _notifier.projectModified(); // not currently used
 432    }
 433   
 434    /** @return true if the project state has been changed. */
 435  4 public boolean isProjectChanged() { return _state.isProjectChanged(); }
 436   
 437    /** @return true if the model has a project open, false otherwise. */
 438  172 public boolean isProjectActive() { return _state.isProjectActive(); }
 439   
 440    /** @return the file that points to the current project file. Null if not currently in project view
 441    */
 442  0 public File getProjectFile() { return _state.getProjectFile(); }
 443   
 444    /** @return all files currently saved as source files in the project file.
 445    * If _state not in project mode, returns null
 446    */
 447  0 public File[] getProjectFiles() { return _state.getProjectFiles(); }
 448   
 449    /** @return true the given file is in the current project file. */
 450  105 public boolean inProject(File f) { return _state.inProject(f); }
 451   
 452    /** A file is in the project if the source root is the same as the
 453    * project root. this means that project files must be saved at the
 454    * source root. (we query the model through the model's state)
 455    */
 456  69 public boolean inProjectPath(OpenDefinitionsDocument doc) { return _state.inProjectPath(doc); }
 457   
 458    /** Sets the class with the project's main method. */
 459  0 public void setMainClass(String f) {
 460  0 _state.setMainClass(f);
 461  0 _notifier.projectRunnableChanged();
 462  0 setProjectChanged(true);
 463    }
 464   
 465    /** @return the class with the project's main method. */
 466  8 public String getMainClass() { return _state.getMainClass(); }
 467   
 468    /** @return the file containing the project's main class. */
 469  0 public File getMainClassContainingFile() {
 470  0 String path = getMainClass();
 471   
 472  0 if (path == null) return null;
 473   
 474    // TODO: What about language level file extensions? What about Habanero Java extension?
 475  0 if (path.toLowerCase().endsWith(OptionConstants.JAVA_FILE_EXTENSION)) {
 476  0 return new File(getProjectFile().getParent(), path);
 477    } //if
 478   
 479    // maybe we have an inner class; remove names from the end and see if we find
 480    // a file for it that way.
 481    // Example:
 482    // some/package/SomeClass/Inner/AnotherInner.java (not found)
 483    // some/package/SomeClass/Inner.java (not found)
 484    // some/package/SomeClass.java (not found)
 485  0 path = path.replace('.', File.separatorChar);
 486  0 File tempFile = new File(getProjectRoot(), path+OptionConstants.JAVA_FILE_EXTENSION);
 487  0 while (path.length() > 0) {
 488  0 if (tempFile.exists()) return tempFile;
 489   
 490  0 if (path.indexOf(File.separatorChar) == -1) break;
 491   
 492  0 path = path.substring(0, path.lastIndexOf(File.separatorChar));
 493  0 tempFile = new File(getProjectRoot(), path + OptionConstants.JAVA_FILE_EXTENSION);
 494    }
 495   
 496  0 return null;
 497    }
 498   
 499    /** Sets the create jar file of the project. */
 500  0 public void setCreateJarFile(File f) {
 501  0 _state.setCreateJarFile(f);
 502  0 setProjectChanged(true);
 503    }
 504   
 505    /** Return the create jar file for the project. If not in project mode, returns null. */
 506  1 public File getCreateJarFile() { return _state.getCreateJarFile(); }
 507   
 508    /** Sets the create jar flags of the project. */
 509  0 public void setCreateJarFlags(int f) {
 510  0 _state.setCreateJarFlags(f);
 511  0 setProjectChanged(true);
 512    }
 513   
 514    /** Return the create jar flags for the project. If not in project mode, returns 0. */
 515  1 public int getCreateJarFlags() { return _state.getCreateJarFlags(); }
 516   
 517    /** @return the root of the project sourc tree (assuming one exists). */
 518  347 public File getProjectRoot() { return _state.getProjectRoot(); }
 519   
 520    /** Sets the class with the project's main method. Degenerate version overridden in DefaultGlobalModel. */
 521  0 public void setProjectRoot(File f) {
 522  0 _state.setProjectRoot(f);
 523    // _notifier.projectRootChanged();
 524  0 setProjectChanged(true);
 525    }
 526   
 527    /** Sets project file to specifed value; used in "Save Project As ..." command in MainFrame. */
 528  1 public void setProjectFile(File f) { _state.setProjectFile(f); }
 529   
 530    /** @return the build directory for the project (assuming one exists). */
 531  393 public File getBuildDirectory() { return _state.getBuildDirectory(); }
 532   
 533    /** @return the stored preferences. */
 534  0 public Map<OptionParser<?>,String> getPreferencesStoredInProject() { return _state.getPreferencesStoredInProject(); }
 535   
 536  0 public void setPreferencesStoredInProject(Map<OptionParser<?>,String> sp) { _state.setPreferencesStoredInProject(sp); }
 537   
 538    /** Sets the class with the project's main method. Degenerate version overridden in DefaultGlobalModel. */
 539  0 public void setBuildDirectory(File f) {
 540  0 _state.setBuildDirectory(f);
 541  0 _notifier.projectBuildDirChanged();
 542  0 setProjectChanged(true);
 543    }
 544   
 545    /** Gets autorfresh status of the project */
 546  32 public boolean getAutoRefreshStatus() { return _state.getAutoRefreshStatus(); }
 547   
 548    /** Sets autofresh status of the project */
 549  0 public void setAutoRefreshStatus(boolean status) { _state.setAutoRefreshStatus(status); }
 550   
 551    /** @return the working directory for the Master JVM (editor and GUI). */
 552  307 public File getMasterWorkingDirectory() {
 553  307 File file;
 554  307 try {
 555    // restore the path from the configuration
 556  307 file = FileOps.getValidDirectory(DrJava.getConfig().getSetting(LAST_DIRECTORY));
 557    }
 558    catch (RuntimeException e) {
 559    // something went wrong, clear the setting and use "user.home"
 560  0 DrJava.getConfig().setSetting(LAST_DIRECTORY, FileOps.NULL_FILE);
 561  0 file = FileOps.getValidDirectory(new File(System.getProperty("user.home", ".")));
 562    }
 563    // update the setting and return it
 564  307 DrJava.getConfig().setSetting(LAST_DIRECTORY, file);
 565  307 return file;
 566    }
 567   
 568    /** @return the working directory for the Slave (Interactions) JVM */
 569  41 public File getWorkingDirectory() { return _state.getWorkingDirectory(); }
 570   
 571    /** Sets the working directory for the project; ignored in flat file model. */
 572  0 public void setWorkingDirectory(File f) {
 573  0 _state.setWorkingDirectory(f);
 574  0 _notifier.projectWorkDirChanged();
 575  0 setProjectChanged(true);
 576    // update the setting
 577  0 DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, _state.getWorkingDirectory());
 578    }
 579   
 580  0 public void cleanBuildDirectory() { _state.cleanBuildDirectory(); }
 581   
 582  0 public List<File> getClassFiles() { return _state.getClassFiles(); }
 583   
 584    /** Helper method used in subsequent anonymous inner class */
 585  0 protected static String getPackageName(String classname) {
 586  0 int index = classname.lastIndexOf(".");
 587  0 if (index != -1) return classname.substring(0, index);
 588  0 else return "";
 589    }
 590   
 591    class ProjectFileGroupingState implements FileGroupingState {
 592   
 593    volatile File _projRoot;
 594    volatile String _mainClass;
 595    volatile File _buildDir;
 596    volatile File _workDir;
 597    volatile File _projectFile;
 598    final File[] _projectFiles;
 599    volatile ArrayList<File> _auxFiles; // distinct from _auxiliaryFiles in ProjectProfile
 600    private volatile ArrayList<File> _exclFiles; // distinct from _excludedFiles in ProjectProile and CompilerErrorPanel
 601    volatile Iterable<AbsRelFile> _projExtraClassPath;
 602    private boolean _isProjectChanged = false;
 603    volatile File _createJarFile;
 604    volatile int _createJarFlags;
 605    volatile boolean _autoRefreshStatus;
 606    final Map<OptionParser<?>,String> _storedPreferences = new HashMap<OptionParser<?>,String>();
 607   
 608    volatile String _manifest = null;
 609   
 610    HashSet<String> _projFilePaths = new HashSet<String>();
 611   
 612    /** Degenerate constructor for a new project; only the file project name is known. */
 613  0 ProjectFileGroupingState(File project) {
 614  0 this(project.getParentFile(), null, null, null, project, new File[0], new File[0], new File[0],
 615    IterUtil.<AbsRelFile>empty(), null, 0, false, null, new HashMap<OptionParser<?>,String>());
 616  0 HashMap<OptionParser<?>,String> defaultStoredPreferences = new HashMap<OptionParser<?>,String>();
 617    // by default, put INDENT_LEVEL AND LANGUAGE_LEVEL into the project file
 618  0 defaultStoredPreferences.put(INDENT_LEVEL, DrJava.getConfig().getOptionMap().getString(INDENT_LEVEL));
 619  0 defaultStoredPreferences.put(LANGUAGE_LEVEL, DrJava.getConfig().getOptionMap().getString(LANGUAGE_LEVEL));
 620  0 setPreferencesStoredInProject(defaultStoredPreferences);
 621    }
 622   
 623  4 ProjectFileGroupingState(File pr, String main, File bd, File wd, File project, File[] srcFiles, File[] auxFiles,
 624    File[] excludedFiles, Iterable<AbsRelFile> cp, File cjf, int cjflags, boolean refreshStatus,
 625    String customManifest, Map<OptionParser<?>,String> storedPreferences) {
 626  4 _projRoot = pr;
 627  4 _mainClass = main;
 628  4 _buildDir = bd;
 629  4 _workDir = wd;
 630  4 _projectFile = project;
 631  4 _projectFiles = srcFiles;
 632  4 _auxFiles = new ArrayList<File>(auxFiles.length);
 633  1 for(File f: auxFiles) { _auxFiles.add(f); }
 634  4 _exclFiles = new ArrayList<File>(excludedFiles.length);
 635  0 for(File f: excludedFiles) { _exclFiles.add(f); }
 636  4 _projExtraClassPath = cp;
 637   
 638  4 if (_projectFiles != null) {
 639  8 try { for (File file : _projectFiles) { _projFilePaths.add(file.getCanonicalPath()); } }
 640    catch(IOException e) { /*do nothing */ }
 641    }
 642   
 643  4 _createJarFile = cjf;
 644  4 _createJarFlags = cjflags;
 645  4 _autoRefreshStatus = refreshStatus;
 646  4 _manifest = customManifest;
 647  4 setPreferencesStoredInProject(storedPreferences);
 648    }
 649   
 650  19 public boolean isProjectActive() { return true; }
 651   
 652    /** Determines whether the specified doc in within the project file tree.
 653    * No synchronization is required because only immutable data is accessed.
 654    */
 655  31 public boolean inProjectPath(OpenDefinitionsDocument doc) {
 656  8 if (doc.isUntitled()) return false;
 657   
 658    /* If the file does not exist, we still want to tell if it's path lies within the project source tree. The file
 659    * may have existed previously at one point and then removed, in which case we should treat it as an untitled
 660    * project file that should be resaved. */
 661  23 File f;
 662  23 try { f = doc.getFile(); }
 663  0 catch(FileMovedException fme) { f = fme.getFile(); }
 664  23 return inProjectPath(f);
 665    }
 666   
 667    /** Determines whether the specified file in within the project file tree. No synchronization is required because
 668    * only immutable data is accessed.
 669    */
 670  38 public boolean inProjectPath(File f) { return IOUtil.isMember(f, getProjectRoot()); }
 671   
 672    /** @return the absolute path to the project file. Since projectFile is final, no synchronization is necessary.*/
 673  0 public File getProjectFile() { return _projectFile; }
 674   
 675  15 public boolean inProject(File f) {
 676  15 String path;
 677   
 678  3 if (isUntitled(f) || ! inProjectPath(f)) return false;
 679  12 try {
 680  12 path = f.getCanonicalPath();
 681  12 return _projFilePaths.contains(path);
 682    }
 683  0 catch(IOException ioe) { return false; }
 684    }
 685   
 686  0 public File[] getProjectFiles() { return _projectFiles; }
 687   
 688  50 public File getProjectRoot() {
 689  0 if (_projRoot == null || _projRoot.equals( FileOps.NULL_FILE)) return _projectFile.getParentFile();
 690    // Utilities.show("File grouping state returning project root of " + _projRoot);
 691  50 return _projRoot;
 692    }
 693   
 694  14 public File getBuildDirectory() { return _buildDir; }
 695   
 696  4 public File getWorkingDirectory() {
 697  4 try {
 698  4 if (_workDir == null || _workDir == FileOps.NULL_FILE) {
 699    // if no project working directory is set, check preferences working directory
 700  4 File prefWorkDir = DrJava.getConfig().getSetting(FIXED_INTERACTIONS_DIRECTORY);
 701  4 if ((prefWorkDir != null) && (prefWorkDir != FileOps.NULL_FILE)) {
 702  0 try {
 703    // make sure it's a valid directory
 704  0 prefWorkDir = FileOps.getValidDirectory(prefWorkDir);
 705    }
 706  0 catch (RuntimeException e) { prefWorkDir = FileOps.NULL_FILE; }
 707    }
 708  0 if ((prefWorkDir != null) && (prefWorkDir != FileOps.NULL_FILE)) { return prefWorkDir; }
 709   
 710    // if there is no fixed working directory in the preferences, use the directory
 711    // containing the project file
 712  4 File parentDir = _projectFile.getParentFile();
 713  4 if (parentDir != null) {
 714  4 return parentDir.getCanonicalFile(); // default is project root
 715    } // or if all else fails, user.dir
 716  0 else return new File(System.getProperty("user.dir"));
 717    }
 718  0 return _workDir.getCanonicalFile();
 719    }
 720    catch(IOException e) { /* fall through */ }
 721  0 return _workDir.getAbsoluteFile();
 722    }
 723   
 724    /** Sets project file to specifed value; used in "Save Project As ..." command in MainFrame. */
 725  1 public void setProjectFile(File f) { _projectFile = f; }
 726   
 727  0 public void setProjectRoot(File f) {
 728  0 _projRoot = f;
 729    // System.err.println("Project root set to " + f);
 730    }
 731   
 732    /** Adds File f to end of _auxFiles vector. */
 733  1 public void addAuxFile(File f) {
 734  1 synchronized(_auxFiles) {
 735  1 if (_auxFiles.add(f)) setProjectChanged(true);
 736    }
 737    }
 738   
 739    /** Removes File file from _auxFiles list. */
 740  1 public void remAuxFile(File file) {
 741  1 synchronized(_auxFiles) {
 742  1 if (_auxFiles.remove(file)) setProjectChanged(true);
 743    }
 744    }
 745   
 746  0 public void addExcludedFile(File f) {
 747  0 if(f == null) return;
 748  0 if (isAlreadyOpen(f)) return; // can't add files to the black list that are currently open
 749  0 synchronized(_exclFiles) {
 750  0 if (_exclFiles.add(f)) setProjectChanged(true);
 751    }
 752    }
 753   
 754  0 public void removeExcludedFile(File f) {
 755  0 synchronized(_exclFiles) {
 756  0 for(int i = 0;i < _exclFiles.size();i++) {
 757  0 try {
 758  0 if(_exclFiles.get(i).getCanonicalPath().equals(f.getCanonicalPath())) {
 759  0 _exclFiles.remove(i);
 760  0 setProjectChanged(true);
 761    }
 762    }
 763    catch(IOException e) { }
 764    }
 765    }
 766    }
 767   
 768  1 public File[] getExclFiles() { return _exclFiles.toArray(new File[_exclFiles.size()]); }
 769   
 770  0 public void setExcludedFiles(File[] fs) {
 771  0 if(fs == null) return;
 772  0 synchronized(_exclFiles) {
 773  0 _exclFiles.clear();
 774  0 for(File f: fs) { addExcludedFile(f); }
 775  0 setProjectChanged(true);
 776    }
 777    }
 778   
 779  1 public void setBuildDirectory(File f) { _buildDir = f; }
 780   
 781  0 public void setWorkingDirectory(File f) { _workDir = f; }
 782   
 783  8 public String getMainClass() { return _mainClass; }
 784   
 785  0 public void setMainClass(String f) { _mainClass = f; }
 786   
 787  0 public void setCreateJarFile(File f) { _createJarFile = f; }
 788   
 789  1 public File getCreateJarFile() { return _createJarFile; }
 790   
 791  0 public void setCreateJarFlags(int f) { _createJarFlags = f; }
 792   
 793  1 public int getCreateJarFlags() { return _createJarFlags; }
 794   
 795  4 public boolean isProjectChanged() { return _isProjectChanged; }
 796   
 797  7 public void setProjectChanged(boolean changed) { _isProjectChanged = changed; }
 798   
 799  24 public boolean isAuxiliaryFile(File f) {
 800  24 String path;
 801   
 802  0 if (isUntitled(f)) return false;
 803   
 804  24 try { path = f.getCanonicalPath();}
 805  0 catch(IOException ioe) { return false; }
 806   
 807  24 synchronized(_auxFiles) {
 808  24 for (File file : _auxFiles) {
 809  6 try { if (file.getCanonicalPath().equals(path)) return true; }
 810    catch(IOException ioe) { /* ignore file */ }
 811    }
 812  18 return false;
 813    }
 814    }
 815   
 816  7 public boolean isExcludedFile(File f) {
 817  7 String path;
 818  0 if (isUntitled(f)) return false;
 819   
 820  7 try { path = f.getCanonicalPath();}
 821  0 catch(IOException ioe) { return false; }
 822   
 823  7 synchronized(_exclFiles) {
 824  7 for (File file : _exclFiles) {
 825  0 try { if (file.getCanonicalPath().equals(path)) return true; }
 826    catch(IOException ioe) { /* ignore file */ }
 827    }
 828  7 return false;
 829    }
 830    }
 831   
 832  5 public boolean getAutoRefreshStatus() { return _autoRefreshStatus; }
 833  0 public void setAutoRefreshStatus(boolean status) { _autoRefreshStatus = status; }
 834   
 835    /** @return the stored preferences. */
 836  1 public Map<OptionParser<?>,String> getPreferencesStoredInProject() {
 837  1 return new HashMap<OptionParser<?>,String>(_storedPreferences);
 838    }
 839   
 840  5 public void setPreferencesStoredInProject(Map<OptionParser<?>,String> sp) {
 841    // remove previous listeners
 842  5 removePreviousListeners();
 843   
 844  5 _storedPreferences.clear();
 845  5 _storedPreferences.putAll(sp);
 846   
 847    // add new listeners
 848  5 addNewListeners(sp);
 849    }
 850   
 851    // This only starts the process. It is all done asynchronously.
 852  0 public void cleanBuildDirectory() {
 853  0 File dir = this.getBuildDirectory ();
 854  0 _notifier.executeAsyncTask(_findFilesToCleanTask, dir, false, true);
 855    }
 856   
 857    private AsyncTask<File,List<File>> _findFilesToCleanTask = new AsyncTask<File,List<File>>("Find Files to Clean") {
 858    private FilenameFilter _filter = new FilenameFilter() {
 859  0 public boolean accept(File parent, String name) {
 860  0 return new File(parent, name).isDirectory() || name.endsWith(".class");
 861    }
 862    };
 863   
 864  0 public List<File> runAsync(File buildDir, IAsyncProgress monitor) throws Exception {
 865  0 List<File> accumulator = new LinkedList<File>();
 866  0 helper(buildDir, accumulator); // adds files to the accumulator recursively
 867  0 return accumulator;
 868    }
 869  0 public void complete(AsyncCompletionArgs<List<File>> args) {
 870  0 _notifier.executeAsyncTask(_deleteFilesTask, args.getResult(), true, true);
 871    }
 872  0 public String getDiscriptionMessage() {
 873  0 return "Finding files to delete...";
 874    }
 875  0 private void helper(File file, List<File> accumulator) {
 876  0 if (file.isDirectory ()) {
 877  0 File[] children = file.listFiles(_filter);
 878  0 for (File child : children) {
 879  0 helper(child, accumulator);
 880  0 accumulator.add(file);
 881    }
 882    }
 883  0 else if ( file.getName().endsWith(".class")) accumulator.add(file);
 884    }
 885    };
 886   
 887    private AsyncTask<List<File>,List<File>> _deleteFilesTask = new AsyncTask<List<File>,List<File>>("Delete Files") {
 888  0 public List<File> runAsync(List<File> filesToDelete, IAsyncProgress monitor) throws Exception {
 889  0 List<File> undeletableFiles = new ArrayList<File>();
 890   
 891  0 monitor.setMinimum (0);
 892  0 monitor.setMaximum(filesToDelete.size());
 893  0 int progress = 1;
 894  0 for(File file : filesToDelete) {
 895  0 if (monitor.isCanceled()) {
 896  0 break;
 897    }
 898  0 monitor.setNote(file.getName());
 899  0 boolean could = file.delete();
 900  0 if (!could) undeletableFiles.add(file);
 901  0 monitor.setProgress(progress++);
 902    }
 903    // if (! dir.exists()) dir.mkdirs (); // TODO: figure out where to put this.
 904  0 return undeletableFiles;
 905    }
 906  0 public void complete(AsyncCompletionArgs<List<File>> args) {
 907    // TODO: user feedback. Maybe add a method to the notifier to set the status bar text
 908    }
 909  0 public String getDiscriptionMessage() {
 910  0 return "Deleting files...";
 911    }
 912    };
 913   
 914  0 public List<File> getClassFiles() {
 915  0 File dir = this.getBuildDirectory ();
 916  0 LinkedList<File> acc = new LinkedList<File>();
 917  0 getClassFilesHelper(dir, acc);
 918  0 if (! dir.exists()) dir.mkdirs(); // TODO: what if mkdirs() fails
 919  0 return acc;
 920    }
 921   
 922  0 private void getClassFilesHelper(File f, LinkedList<File> acc) {
 923  0 if (f.isDirectory()) {
 924   
 925  0 File fs[] = f.listFiles(new FilenameFilter() {
 926  0 public boolean accept(File parent, String name) {
 927  0 return new File(parent, name).isDirectory() || name.endsWith(".class");
 928    }
 929    });
 930   
 931  0 if (fs != null) { // listFiles may return null if there's an IO error
 932  0 for (File kid: fs) { getClassFilesHelper(kid, acc); }
 933    }
 934   
 935  0 } else if (f.getName().endsWith(".class")) acc.add(f);
 936    }
 937   
 938    // ----- FIND ALL DEFINED CLASSES IN FOLDER ---
 939   
 940  4 public Iterable<AbsRelFile> getExtraClassPath() { return _projExtraClassPath; }
 941  0 public void setExtraClassPath(Iterable<AbsRelFile> cp) {
 942  0 _projExtraClassPath = cp;
 943  0 setClassPathChanged(true);
 944    }
 945   
 946    // ---- Custom Manifest methods -- ///
 947  1 public String getCustomManifest() { return _manifest; }
 948  0 public void setCustomManifest(String manifest) { _manifest = manifest; }
 949    }
 950   
 951  7 @SuppressWarnings("unchecked")
 952    protected void removePreviousListeners() {
 953  7 for(Map.Entry<OptionParser<?>, OptionListener<?>> e: LISTENERS_TO_REMOVE.entrySet()) {
 954    // all keys should be full Option instances, not just OptionParser instances
 955  0 if (e.getKey() instanceof Option) {
 956  0 DrJava.getConfig().removeOptionListener((Option)e.getKey(), e.getValue());
 957    }
 958    }
 959  7 LISTENERS_TO_REMOVE.clear();
 960    }
 961   
 962  5 @SuppressWarnings("unchecked")
 963    protected void addNewListeners(Map<OptionParser<?>,String> newValues) {
 964  5 for(OptionParser<?> key: newValues.keySet()) {
 965    // all keys should be full Option instances, not just OptionParser instances
 966  0 if (key instanceof Option) {
 967  0 DrJava.getConfig().addOptionListener((Option)key, STORED_PREFERENCES_LISTENER);
 968  0 LISTENERS_TO_REMOVE.put(key, STORED_PREFERENCES_LISTENER);
 969    }
 970    }
 971    }
 972   
 973    protected static final HashMap<OptionParser<?>, OptionListener<? extends Object>> LISTENERS_TO_REMOVE =
 974    new HashMap<OptionParser<?>, OptionListener<? extends Object>>();
 975   
 976    public final OptionListener<? extends Object> STORED_PREFERENCES_LISTENER = new OptionListener<Object>() {
 977  0 public void optionChanged(OptionEvent<Object> oce) {
 978  0 setProjectChanged(true);
 979    }
 980    };
 981   
 982  161 protected FileGroupingState makeFlatFileGroupingState() { return new FlatFileGroupingState(); }
 983   
 984    class FlatFileGroupingState implements FileGroupingState {
 985  404 public File getBuildDirectory() { return FileOps.NULL_FILE; }
 986  335 public File getProjectRoot() { return getWorkingDirectory(); }
 987  372 public File getWorkingDirectory() {
 988    // if a fixed working directory has been set in the Preferences, use it
 989  372 File prefWorkDir = DrJava.getConfig().getSetting(FIXED_INTERACTIONS_DIRECTORY);
 990  372 if ((prefWorkDir != null) && (prefWorkDir != FileOps.NULL_FILE)) {
 991  0 try {
 992    // make sure it's a valid directory
 993  0 prefWorkDir = FileOps.getValidDirectory(prefWorkDir);
 994    }
 995  0 catch (RuntimeException e) { prefWorkDir = FileOps.NULL_FILE; }
 996    }
 997  372 if ((prefWorkDir != null) && (prefWorkDir != FileOps.NULL_FILE)) {
 998    // update the setting and return it
 999  0 DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, prefWorkDir);
 1000  0 return prefWorkDir;
 1001    }
 1002   
 1003    // otherwise determine the working directory based on the source root
 1004  372 File file = FileOps.NULL_FILE;
 1005  372 try {
 1006  372 file = getActiveDocument().getSourceRoot(); // source root of the current document
 1007    }
 1008  268 catch(InvalidPackageException ipe) { file = FileOps.NULL_FILE; }
 1009  372 if ((file != null) && (file != FileOps.NULL_FILE)) {
 1010    // update the setting and return it
 1011  104 DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, file);
 1012  104 return file;
 1013    }
 1014   
 1015    // if we can't get the source root of the current document, use the first document
 1016  268 Iterable<File> roots = getSourceRootSet();
 1017  35 if (!IterUtil.isEmpty(roots)) { return IterUtil.first(roots); }
 1018    else {
 1019    // use the last directory saved to the configuration
 1020  233 if (DrJava.getConfig().getSetting(STICKY_INTERACTIONS_DIRECTORY)) {
 1021  233 try {
 1022    // restore the path from the configuration
 1023  233 file = FileOps.getValidDirectory(DrJava.getConfig().getSetting(LAST_INTERACTIONS_DIRECTORY));
 1024    }
 1025  0 catch (RuntimeException e) { file = FileOps.NULL_FILE; }
 1026    }
 1027  233 if (file == FileOps.NULL_FILE) {
 1028    // something went wrong, clear the setting and use "user.home"
 1029  0 file = FileOps.getValidDirectory(new File(System.getProperty("user.home", ".")));
 1030    }
 1031    // update the setting and return it
 1032  233 DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, file);
 1033  233 return file;
 1034    }
 1035    }
 1036  153 public boolean isProjectActive() { return false; }
 1037  281 public boolean inProjectPath(OpenDefinitionsDocument doc) { return false; }
 1038  0 public boolean inProjectPath(File f) { return false; }
 1039  0 public File getProjectFile() { return FileOps.NULL_FILE; }
 1040  1 public void setBuildDirectory(File f) { }
 1041  0 public void setProjectFile(File f) { }
 1042  0 public void setProjectRoot(File f) { }
 1043  0 public void addAuxFile(File f) { }
 1044  0 public void remAuxFile(File f) { }
 1045  0 public void setWorkingDirectory(File f) { }
 1046  0 public File[] getProjectFiles() { return new File[0]; }
 1047  91 public boolean inProject(File f) { return false; }
 1048  0 public String getMainClass() { return null; }
 1049  0 public void setMainClass(String f) { }
 1050  0 public void setCreateJarFile(File f) { }
 1051  0 public File getCreateJarFile() { return FileOps.NULL_FILE; }
 1052  0 public void setCreateJarFlags(int f) { }
 1053  0 public int getCreateJarFlags() { return 0; }
 1054  180 public Iterable<AbsRelFile> getExtraClassPath() { return IterUtil.empty(); }
 1055  0 public void setExtraClassPath(Iterable<AbsRelFile> cp) { }
 1056  0 public boolean isProjectChanged() { return false; }
 1057  127 public void setProjectChanged(boolean changed) { /* Do nothing */ }
 1058  256 public boolean isAuxiliaryFile(File f) { return false; }
 1059  22 public boolean isExcludedFile(File f) { return false; }
 1060  0 public File[] getExclFiles() { return null; }
 1061  0 public void addExcludedFile(File f) { }
 1062  0 public void removeExcludedFile(File f) { }
 1063  0 public void setExcludedFiles(File[] fs) { }
 1064  31 public boolean getAutoRefreshStatus() {return false;}
 1065  0 public void setAutoRefreshStatus(boolean b) { }
 1066  0 public void setPreferencesStoredInProject(Map<OptionParser<?>,String> sp) { /* do nothing */ }
 1067  0 public Map<OptionParser<?>,String> getPreferencesStoredInProject() { return new HashMap<OptionParser<?>,String>(); }
 1068   
 1069  0 public void cleanBuildDirectory() { }
 1070   
 1071  0 public List<File> getClassFiles() { return new LinkedList<File>(); }
 1072   
 1073  0 public String getCustomManifest() { return null; }
 1074  0 public void setCustomManifest(String manifest) { }
 1075    }
 1076   
 1077    /** Gives the title of the source bin for the navigator.
 1078    * @return The text used for the source bin in the tree navigator
 1079    */
 1080  3 public String getSourceBinTitle() { return "[ Source Files ]"; }
 1081   
 1082    /** Gives the title of the external files bin for the navigator
 1083    * @return The text used for the external files bin in the tree navigator.
 1084    */
 1085  3 public String getExternalBinTitle() { return "[ External Files ]"; }
 1086   
 1087    /** Gives the title of the aux files bin for the navigator.
 1088    * @return The text used for the aux files bin in the tree navigator.
 1089    */
 1090  3 public String getAuxiliaryBinTitle() { return "[ Included External Files ]"; }
 1091   
 1092    // ----- METHODS -----
 1093   
 1094    /** Add a listener to this global model.
 1095    * @param listener a listener that reacts on events generated by the GlobalModel.
 1096    */
 1097  261 public void addListener(GlobalModelListener listener) { _notifier.addListener(listener); }
 1098   
 1099    /** Remove a listener from this global model.
 1100    * @param listener a listener that reacts on events generated by the GlobalModel
 1101    * This method is synchronized using the readers/writers event protocol incorporated in EventNotifier<T>.
 1102    */
 1103  191 public void removeListener(GlobalModelListener listener) { _notifier.removeListener(listener); }
 1104   
 1105    // getter methods for the private fields
 1106   
 1107  38 public DefinitionsEditorKit getEditorKit() { return _editorKit; }
 1108   
 1109    /** throws UnsupportedOperationException */
 1110  0 public DefaultInteractionsModel getInteractionsModel() {
 1111  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support interaction");
 1112    }
 1113   
 1114    /** throws UnsupportedOperationException */
 1115  0 public InteractionsDJDocument getSwingInteractionsDocument() {
 1116  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support interaction");
 1117    }
 1118   
 1119    /** throws UnsupportedOperationException */
 1120  0 public InteractionsDocument getInteractionsDocument() {
 1121  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support interaction");
 1122    }
 1123   
 1124  40 public ConsoleDocument getConsoleDocument() { return _consoleDoc; }
 1125   
 1126  38 public InteractionsDJDocument getSwingConsoleDocument() { return _consoleDocAdapter; }
 1127   
 1128  0 public PageFormat getPageFormat() { return _pageFormat; }
 1129   
 1130  0 public void setPageFormat(PageFormat format) { _pageFormat = format; }
 1131   
 1132  0 public CompilerModel getCompilerModel() {
 1133  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support compilation");
 1134    }
 1135   
 1136    /** throws UnsupportedOperationException */
 1137  0 public int getNumCompErrors() {
 1138  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support compilation");
 1139    }
 1140   
 1141    /** throws UnsupportedOperationException */
 1142  0 public void setNumCompErrors(int num) {
 1143  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support compilation");
 1144    };
 1145   
 1146    /** throws UnsupportedOperationException */
 1147  0 public JUnitModel getJUnitModel() {
 1148  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support unit testing");
 1149    }
 1150   
 1151    /** throws UnsupportedOperationException */
 1152  0 public JavadocModel getJavadocModel() {
 1153  0 throw new UnsupportedOperationException("AbstractGlobalModel does not support javadoc");
 1154    }
 1155   
 1156  205 public IDocumentNavigator<OpenDefinitionsDocument> getDocumentNavigator() { return _documentNavigator; }
 1157   
 1158  5 public void setDocumentNavigator(IDocumentNavigator<OpenDefinitionsDocument> newnav) { _documentNavigator = newnav; }
 1159   
 1160    /** Toogle the specified bookmark in the active document.
 1161    * @param pos1 first selection position
 1162    * @param pos2 second selection position */
 1163  8 public void toggleBookmark(int pos1, int pos2) { _toggleBookmark(pos1, pos2); }
 1164   
 1165    /** Raw version of toggleBookmark. ASSUMES that read lock is already held
 1166    * @param pos1 first selection position
 1167    * @param pos2 second selection position */
 1168  8 public void _toggleBookmark(int pos1, int pos2) {
 1169    // Utilities.show("AGM.toggleBookmark called");
 1170  8 assert EventQueue.isDispatchThread();
 1171   
 1172  8 final OpenDefinitionsDocument doc = getActiveDocument();
 1173   
 1174  8 int startSel = Math.min(pos1, pos2);
 1175  8 int endSel = Math.max(pos1, pos2);
 1176    // try {
 1177  8 RegionManager<MovingDocumentRegion> bm = _bookmarkManager;
 1178  8 if (startSel == endSel) { // offset only; bookmark the entire line
 1179  2 endSel = doc._getLineEndPos(startSel);
 1180  2 startSel = doc._getLineStartPos(startSel);
 1181    }
 1182   
 1183  8 Collection<MovingDocumentRegion> conflictingRegions = bm.getRegionsOverlapping(doc, startSel, endSel);
 1184   
 1185  8 if (conflictingRegions.size() > 0) {
 1186  5 for (MovingDocumentRegion cr: conflictingRegions) bm.removeRegion(cr);
 1187    }
 1188    else {
 1189  5 MovingDocumentRegion newR =
 1190    new MovingDocumentRegion(doc, startSel, endSel, doc._getLineStartPos(startSel), doc._getLineEndPos(endSel));
 1191  5 bm.addRegion(newR);
 1192    }
 1193    }
 1194   
 1195    /** Creates a new open definitions document and adds it to the list. Public for testing purposes. Only runs in
 1196    * the event thread.
 1197    * @param parentDir directory in which the document should be located
 1198    * @return The new open document
 1199    */
 1200  253 public OpenDefinitionsDocument newFile(File parentDir) { return newFile(parentDir, ""); }
 1201   
 1202    /** Creates a new open definitions document and adds it to the list. Public for testing purposes. Only runs in
 1203    * the event thread.
 1204    * @param parentDir directory in which the document should be located
 1205    * @param text text for the new document
 1206    * @return The new open document
 1207    */
 1208  424 public OpenDefinitionsDocument newFile(File parentDir, String text) {
 1209    ///* */ assert Utilities.TEST_MODE || EventQueue.isDispatchThread();
 1210  424 final ConcreteOpenDefDoc doc = _createOpenDefinitionsDocument(new NullFile());
 1211  424 try {
 1212  424 if (text.length() > 0) {
 1213  0 doc.insertString(0, text, null);
 1214  0 doc.indentLines(0, text.length());
 1215    }
 1216    }
 1217    catch (BadLocationException ble) {
 1218  0 throw new UnexpectedException(ble);
 1219    }
 1220    finally {
 1221  424 doc.setParentDirectory(parentDir);
 1222  424 addDocToNavigator(doc);
 1223  424 _notifier.newFileCreated(doc);
 1224    }
 1225  424 return doc;
 1226    }
 1227   
 1228    /** Creates a new document, adds it to the list of open documents, and sets it to be active.
 1229    * @return The new open document
 1230    */
 1231  171 public OpenDefinitionsDocument newFile(String text) {
 1232  171 File dir = _activeDirectory;
 1233  0 if (dir == null) dir = getMasterWorkingDirectory();
 1234  171 OpenDefinitionsDocument doc = newFile(dir, text);
 1235  171 setActiveDocument(doc);
 1236  171 return doc;
 1237    }
 1238   
 1239    /** Creates a new document, adds it to the list of open documents, and sets it to be active.
 1240    * @return The new open document
 1241    */
 1242  171 public OpenDefinitionsDocument newFile() { return newFile(""); }
 1243   
 1244    /** Creates a new junit test case.
 1245    * @param name the name of the new test case
 1246    * @param makeSetUp true iff an empty setUp() method should be included
 1247    * @param makeTearDown true iff an empty tearDown() method should be included
 1248    * @return the new open test case
 1249    */
 1250  0 public OpenDefinitionsDocument newTestCase(String name, boolean makeSetUp, boolean makeTearDown) {
 1251  0 boolean elementary =
 1252    (DrJava.getConfig().getSetting(LANGUAGE_LEVEL) == OptionConstants.ELEMENTARY_LEVEL) ||
 1253    (DrJava.getConfig().getSetting(LANGUAGE_LEVEL) == OptionConstants.FUNCTIONAL_JAVA_LEVEL);
 1254   
 1255  0 final StringBuilder buf = new StringBuilder();
 1256  0 if (! elementary) buf.append("import junit.framework.TestCase;\n\n");
 1257  0 buf.append("/**\n");
 1258  0 buf.append("* A JUnit test case class.\n");
 1259  0 buf.append("* Every method starting with the word \"test\" will be called when running\n");
 1260  0 buf.append("* the test with JUnit.\n");
 1261  0 buf.append("*/\n");
 1262  0 if (! elementary) buf.append("public ");
 1263  0 buf.append("class ");
 1264  0 buf.append(name);
 1265  0 buf.append(" extends TestCase {\n\n");
 1266  0 if (makeSetUp) {
 1267  0 buf.append("/**\n");
 1268  0 buf.append("* This method is called before each test method, to perform any common\n");
 1269  0 buf.append("* setup if necessary.\n");
 1270  0 buf.append("*/\n");
 1271  0 if (! elementary) buf.append("public ");
 1272  0 buf.append("void setUp() throws Exception {\n}\n\n");
 1273    }
 1274  0 if (makeTearDown) {
 1275  0 buf.append("/**\n");
 1276  0 buf.append("* This method is called after each test method, to perform any common\n");
 1277  0 buf.append("* clean-up if necessary.\n");
 1278  0 buf.append("*/\n");
 1279  0 if (! elementary) buf.append("public ");
 1280  0 buf.append("void tearDown() throws Exception {\n}\n\n");
 1281    }
 1282  0 buf.append("/**\n");
 1283  0 buf.append("* A test method.\n");
 1284  0 buf.append("* (Replace \"X\" with a name describing the test. You may write as\n");
 1285  0 buf.append ("* many \"testSomething\" methods in this class as you wish, and each\n");
 1286  0 buf.append("* one will be called when running JUnit over this class.)\n");
 1287  0 buf.append("*/\n");
 1288  0 if (! elementary) buf.append("public ");
 1289  0 buf.append("void testX() {\n}\n\n");
 1290  0 buf.append("}\n");
 1291  0 String test = buf.toString();
 1292   
 1293  0 OpenDefinitionsDocument openDoc = newFile();
 1294  0 try {
 1295  0 openDoc.insertString(0, test, null);
 1296  0 openDoc.indentLines(0, test.length());
 1297    }
 1298    catch (BadLocationException ble) {
 1299  0 throw new UnexpectedException(ble);
 1300    }
 1301  0 return openDoc;
 1302    }
 1303   
 1304    /** This method is for use only by test cases. */
 1305  7 public DocumentCache getDocumentCache() { return _cache; }
 1306   
 1307    //---------------------- Specified by ILoadDocuments ----------------------//
 1308   
 1309    /** Open a file and add it to the pool of definitions documents. The provided file selector chooses a file,
 1310    * and on a successful open, the fileOpened() event is fired. This method also checks if there was previously
 1311    * a single unchanged, untitled document open, and if so, closes it after a successful opening.
 1312    * @param com a command pattern command that selects what file to open
 1313    * @return The open document, or null if unsuccessful
 1314    * @exception IOException
 1315    * @exception OperationCanceledException if the open was canceled
 1316    * @exception AlreadyOpenException if the file is already open
 1317    */
 1318  41 public OpenDefinitionsDocument openFile(FileOpenSelector com) throws
 1319    IOException, OperationCanceledException, AlreadyOpenException {
 1320    // Close an untitled, unchanged document if it is the only one open
 1321  41 boolean closeUntitled = _hasOneEmptyDocument();
 1322  20 if (! closeUntitled) addToBrowserHistory();
 1323   
 1324  41 OpenDefinitionsDocument oldDoc = _activeDocument;
 1325  41 OpenDefinitionsDocument openedDoc = openFileHelper(com);
 1326  19 if (closeUntitled) closeFileHelper(oldDoc);
 1327    // Utilities.showDebug("DrJava has opened" + openedDoc + " and is setting it active");
 1328    // addToBrowserHistory();
 1329  29 setActiveDocument(openedDoc);
 1330  29 setProjectChanged(true);
 1331    // Utilities.showDebug("active doc set; openFile returning");
 1332  29 return openedDoc;
 1333    }
 1334   
 1335  58 protected OpenDefinitionsDocument openFileHelper(FileOpenSelector com) throws IOException,
 1336    OperationCanceledException, AlreadyOpenException {
 1337   
 1338    // This code is duplicated in MainFrame._setCurrentDirectory(File) for safety.
 1339  58 final File file = (com.getFiles())[0].getCanonicalFile(); // may throw an IOException if path is invalid
 1340  57 OpenDefinitionsDocument odd = _openFile(file);
 1341    // Utilities.showDebug("File " + file + " opened");
 1342    // Make sure this is on the classpath
 1343  46 addDocToClassPath(odd); // Redundant; done in _openFile
 1344  46 setClassPathChanged(true);
 1345  46 return odd;
 1346    }
 1347   
 1348    /** Open multiple files and add them to the pool of definitions documents. The provided file selector chooses
 1349    * a collection of files, and on successfully opening each file, the fileOpened() event is fired. This method
 1350    * also checks if there was previously a single unchanged, untitled document open, and if so, closes it after
 1351    * a successful opening.
 1352    * @param com a command pattern command that selects what file to open
 1353    * @return The open document, or null if unsuccessful
 1354    * @exception IOException
 1355    * @exception OperationCanceledException if the open was canceled
 1356    * @exception AlreadyOpenException if the file is already open
 1357    */
 1358  16 public OpenDefinitionsDocument[] openFiles(FileOpenSelector com)
 1359    throws IOException, OperationCanceledException, AlreadyOpenException {
 1360   
 1361    // Close an untitled, unchanged document if it is the only one open
 1362  16 boolean closeUntitled = _hasOneEmptyDocument();
 1363  2 if (! closeUntitled) addToBrowserHistory();
 1364  16 OpenDefinitionsDocument oldDoc = _activeDocument;
 1365   
 1366  16 OpenDefinitionsDocument[] openedDocs = openFilesHelper(com);
 1367  13 if (openedDocs.length > 0) {
 1368  12 if (closeUntitled) closeFileHelper(oldDoc);
 1369    // addToBrowserHistory();
 1370  13 setActiveDocument(openedDocs[0]);
 1371    }
 1372  13 return openedDocs;
 1373    }
 1374   
 1375  16 protected OpenDefinitionsDocument[] openFilesHelper(FileOpenSelector com)
 1376    throws IOException, OperationCanceledException, AlreadyOpenException {
 1377   
 1378  16 final File[] files = com.getFiles();
 1379  1 if (files == null) { throw new IOException("No Files returned from FileSelector"); }
 1380  14 OpenDefinitionsDocument[] docs = _openFiles(files);
 1381  13 return docs;
 1382    }
 1383   
 1384    // if set to true, and uncommented, the definitions document will
 1385    // print out a small stack trace every time getDocument() is called
 1386   
 1387    // static boolean SHOW_GETDOC = false;
 1388   
 1389    /** Opens all the files in the list, and notifies about the last file opened. */
 1390  14 private OpenDefinitionsDocument[] _openFiles(File[] files)
 1391    throws IOException, OperationCanceledException, AlreadyOpenException {
 1392   
 1393  14 ArrayList<OpenDefinitionsDocument> alreadyOpenDocuments = new ArrayList<OpenDefinitionsDocument>();
 1394  14 ArrayList<OpenDefinitionsDocument> retDocs = new ArrayList<OpenDefinitionsDocument>();
 1395   
 1396    // SHOW_GETDOC = true;
 1397   
 1398  14 LinkedList<File> filesNotFound = new LinkedList<File>();
 1399  14 LinkedList<OpenDefinitionsDocument> filesOpened = new LinkedList<OpenDefinitionsDocument>();
 1400  14 for (final File f: files) {
 1401  1 if (f == null) throw new IOException("File name returned from FileSelector is null");
 1402  30 try {
 1403  30 OpenDefinitionsDocument d = _rawOpenFile(IOUtil.attemptCanonicalFile(f));
 1404    //always return last opened Doc
 1405  29 retDocs.add(d);
 1406  29 filesOpened.add(d);
 1407  29 if(_state.isExcludedFile(f))
 1408  0 _state.removeExcludedFile(f);
 1409    }
 1410    catch (AlreadyOpenException aoe) {
 1411  0 OpenDefinitionsDocument d = aoe.getOpenDocument();
 1412  0 retDocs.add(d);
 1413  0 alreadyOpenDocuments.add(d);
 1414    }
 1415  1 catch(FileNotFoundException e) { filesNotFound.add(f); }
 1416    }
 1417   
 1418  13 for (final OpenDefinitionsDocument d: filesOpened) {
 1419  29 _completeOpenFile(d); // contains view-related calls
 1420    }
 1421    // SHOW_GETDOC = false;
 1422  13 if (filesNotFound.size() > 0)
 1423  1 _notifier.filesNotFound( filesNotFound.toArray( new File[filesNotFound.size()] ) );
 1424   
 1425  13 if (! alreadyOpenDocuments.isEmpty()) {
 1426  0 for(OpenDefinitionsDocument d : alreadyOpenDocuments) {
 1427  0 _notifier.handleAlreadyOpenDocument(d);
 1428  0 _notifier.fileOpened(d);
 1429    }
 1430    }
 1431   
 1432  13 if (retDocs != null) {
 1433  13 return retDocs.toArray(new OpenDefinitionsDocument[0]);
 1434    }
 1435    else {
 1436    //if we didn't open any files, then it's just as if they cancelled it...
 1437  0 throw new OperationCanceledException();
 1438    }
 1439    }
 1440   
 1441   
 1442    //----------------------- End ILoadDocuments Methods -----------------------//
 1443   
 1444    /** Opens all files in the specified folder dir and places them in the appropriate places in the document navigator.
 1445    * If "open folders recursively" is checked, this operation opens all files in the subtree rooted at dir.
 1446    */
 1447  0 public void openFolder(File dir, boolean rec, String ext)
 1448    throws IOException, OperationCanceledException, AlreadyOpenException {
 1449  0 debug.logStart();
 1450   
 1451  0 final File[] sfiles = getFilesInFolder(dir, rec, ext);
 1452  0 if(sfiles == null) return;
 1453  0 openFiles(new FileOpenSelector() { public File[] getFiles() { return sfiles; } });
 1454   
 1455  0 if (sfiles.length > 0 && _state.inProjectPath(dir)) setProjectChanged(true);
 1456   
 1457  0 debug.logEnd();
 1458    }
 1459   
 1460    /** @return the file extension for the "Open Folder..." command for the currently selected compiler. */
 1461  0 public String getOpenAllFilesInFolderExtension() {
 1462  0 CompilerModel cm = getCompilerModel();
 1463  0 if (cm==null) {
 1464  0 return OptionConstants.LANGUAGE_LEVEL_EXTENSIONS[DrJava.getConfig().getSetting(LANGUAGE_LEVEL)];
 1465    }
 1466    else {
 1467  0 return cm.getActiveCompiler().getOpenAllFilesInFolderExtension();
 1468    }
 1469    }
 1470   
 1471  0 public File[] getFilesInFolder(File dir, boolean rec, String ext) throws IOException, OperationCanceledException,
 1472    AlreadyOpenException {
 1473   
 1474  0 if (dir == null || !dir.isDirectory()) return null; // just in case
 1475   
 1476  0 Iterable<File> filesIterable;
 1477   
 1478  0 String extension = ext.substring(1); // do not include the dot ("java", not ".java")
 1479   
 1480  0 Predicate<File> match = LambdaUtil.and(IOUtil.IS_FILE, IOUtil.extensionFilePredicate(extension));
 1481  0 if (rec) { filesIterable = IOUtil.listFilesRecursively(dir, match); }
 1482  0 else { filesIterable = IOUtil.attemptListFilesAsIterable(dir, match); }
 1483  0 List<File> files = CollectUtil.makeList(filesIterable);
 1484   
 1485  0 if (isProjectActive()) {
 1486  0 Collections.sort(files, new Comparator<File>() {
 1487  0 public int compare(File o1,File o2) {
 1488  0 return - o1.getAbsolutePath().compareTo(o2.getAbsolutePath());
 1489    }
 1490    });
 1491    }
 1492    else {
 1493  0 Collections.sort(files, new Comparator<File>() {
 1494  0 public int compare(File o1,File o2) {
 1495  0 return - o1.getName().compareTo(o2.getName());
 1496    }
 1497    });
 1498    }
 1499  0 int ct = files.size();
 1500   
 1501  0 return files.toArray(new File[ct]);
 1502    }
 1503   
 1504    /** gets files in the project source directory that are not accounted for in the project file.
 1505    * @return null if not in project mode
 1506    */
 1507  0 public File[] getNewFilesInProject() {
 1508   
 1509  0 ArrayList<File> files = new ArrayList<File>();
 1510  0 File projRoot = _state.getProjectRoot();
 1511  0 if(projRoot == null)
 1512  0 return null;
 1513  0 File[] allFiles;
 1514  0 try {
 1515  0 allFiles = getFilesInFolder(projRoot, true, getOpenAllFilesInFolderExtension());
 1516  0 } catch(IOException e) { return null; }
 1517  0 catch(OperationCanceledException e) { return null; }
 1518  0 catch(AlreadyOpenException e) { return null; }
 1519   
 1520  0 for(File f : allFiles) {
 1521  0 if(!isAlreadyOpen(f) && !_state.isExcludedFile(f)) {
 1522  0 files.add(f);
 1523    }
 1524    }
 1525   
 1526  0 return files.toArray(new File[files.size()]);
 1527    }
 1528   
 1529    /** Searches the source folder (recursively) for new files and opens them.
 1530    */
 1531  0 public void openNewFilesInProject() {
 1532  0 File[] newFiles = getNewFilesInProject();
 1533  0 if (newFiles == null) return;
 1534  0 try { _openFiles(newFiles); }
 1535    catch(Exception e) { }
 1536    }
 1537   
 1538   
 1539    /** Saves all open files, prompting for names if necessary.
 1540    * When prompting (i.e., untitled document), set that document as active.
 1541    * @param com a FileSaveSelector
 1542    * @exception IOException
 1543    */
 1544  4 public void saveAllFiles(FileSaveSelector com) throws IOException {
 1545    // OpenDefinitionsDocument curdoc = getActiveDocument();
 1546  4 saveAllFilesHelper(com);
 1547  4 refreshActiveDocument(); // Return focus to previously active doc
 1548    }
 1549   
 1550    /** Called by saveAllFiles in DefaultGlobalModel */
 1551  4 protected void saveAllFilesHelper(FileSaveSelector com) throws IOException {
 1552  4 boolean first = true;
 1553  4 boolean isProjActive = isProjectActive();
 1554   
 1555  4 List<OpenDefinitionsDocument> docsToWrite = getOpenDefinitionsDocuments();
 1556  4 while(docsToWrite.size() > 0) {
 1557  4 ArrayList<OpenDefinitionsDocument> readOnlyDocs = new ArrayList<OpenDefinitionsDocument>();
 1558  4 for (final OpenDefinitionsDocument doc: docsToWrite) { // getOpen... makes a copy
 1559    // do not force Untitled document to be saved if projectActive() or unmodified
 1560  2 if (doc.isUntitled() && (isProjActive || ! doc.isModifiedSinceSave())) continue;
 1561  7 try {
 1562  7 final File docFile = doc.getFile();
 1563  7 if (docFile == null || !docFile.exists() || docFile.canWrite()) {
 1564    // file is writable, save
 1565  7 aboutToSaveFromSaveAll(doc);
 1566  7 doc.saveFile(com);
 1567    }
 1568  0 else if (first) {
 1569    // file is read-only, ask user about it once
 1570  0 readOnlyDocs.add(doc);
 1571    }
 1572    }
 1573    catch(FileMovedException fme) {
 1574    // file was moved, but we should still be able to save it
 1575  0 aboutToSaveFromSaveAll(doc);
 1576  0 doc.saveFile(com);
 1577    }
 1578    }
 1579  4 docsToWrite.clear();
 1580  4 if (readOnlyDocs.size() > 0) {
 1581  0 ArrayList<File> files = new ArrayList<File>();
 1582  0 for(OpenDefinitionsDocument odd: readOnlyDocs) {
 1583  0 try {
 1584  0 File roFile = odd.getFile();
 1585  0 files.add(roFile);
 1586    }
 1587    catch(FileMovedException fme) { /* ignore, don't know what to do here */ }
 1588    }
 1589  0 File[] res = _notifier.filesReadOnly(files.toArray(new File[files.size()]));
 1590  0 HashSet<File> rewriteFiles = new HashSet<File>(java.util.Arrays.asList(res));
 1591  0 for(OpenDefinitionsDocument odd: readOnlyDocs) {
 1592  0 File roFile = odd.getFile();
 1593  0 if (rewriteFiles.contains(roFile)) {
 1594  0 docsToWrite.add(odd);
 1595  0 FileOps.makeWritable(roFile);
 1596    }
 1597    }
 1598    }
 1599  4 first = false;
 1600    }
 1601    }
 1602   
 1603    /** Creates a new FileGroupingState for specificed project file and default values for other properties.
 1604    * @param projFile the new project file (which does not yet exist in the file system)
 1605    */
 1606  0 public void createNewProject(File projFile) { setFileGroupingState(new ProjectFileGroupingState(projFile)); }
 1607   
 1608    /** Configures a new project (created by createNewProject) and writes it to disk; only runs in event thread. */
 1609  0 public void configNewProject() throws IOException {
 1610   
 1611  0 assert EventQueue.isDispatchThread();
 1612   
 1613    // FileGroupingState oldState = _state;
 1614  0 File projFile = getProjectFile();
 1615   
 1616  0 ProjectProfile builder = new ProjectProfile(projFile);
 1617   
 1618    // FileLists for project file
 1619  0 File projectRoot = builder.getProjectRoot();
 1620   
 1621    // Utilities.show("Fetched project root is " + projectRoot);
 1622   
 1623    // List<File> exCp = new LinkedList<File>(); // not used
 1624   
 1625  0 for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
 1626   
 1627  0 File f = doc.getFile();
 1628   
 1629  0 if (!doc.isUntitled()) {
 1630  0 if (IOUtil.isMember(f, projectRoot)) {
 1631  0 DocFile file = new DocFile(f);
 1632  0 file.setPackage(doc.getPackageName()); // must save _packageName so it is correct when project is loaded
 1633  0 builder.addSourceFile(file);
 1634    }
 1635  0 else if ( doc.isAuxiliaryFile()) {
 1636  0 DocFile file = new DocFile(f);
 1637  0 file.setPackage(doc.getPackageName()); // must save _packageName so it is correct when project is loaded
 1638  0 builder.addAuxiliaryFile(new DocFile(f));
 1639    }
 1640    }
 1641    }
 1642   
 1643    // write to disk
 1644  0 builder.write();
 1645   
 1646  0 _loadProject(builder);
 1647    }
 1648   
 1649    /** Writes the project profile augmented by usage info to specified file. Assumes DrJava is in project mode.
 1650    * @param file where to save the project
 1651    * @param info
 1652    */
 1653  1 @SuppressWarnings("unchecked")
 1654    public ProjectProfile _makeProjectProfile(File file, HashMap<OpenDefinitionsDocument, DocumentInfoGetter> info)
 1655    throws IOException {
 1656  1 ProjectProfile builder = new ProjectProfile(file);
 1657   
 1658    // add project root
 1659  1 File pr = getProjectRoot();
 1660  1 if (pr != null) builder.setProjectRoot(pr);
 1661   
 1662    // add opendefinitionsdocument
 1663  1 for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
 1664  3 if (doc.inProjectPath()) {
 1665  2 DocumentInfoGetter g = info.get(doc);
 1666  2 builder.addSourceFile(g);
 1667    }
 1668  1 else if (doc.isAuxiliaryFile()) {
 1669  1 DocumentInfoGetter g = info.get(doc);
 1670  1 builder.addAuxiliaryFile(g);
 1671    }
 1672    }
 1673   
 1674    // add collapsed path info
 1675  1 if (_documentNavigator instanceof JTreeSortNavigator<?>) {
 1676  1 String[] paths = ((JTreeSortNavigator<?>)_documentNavigator).getCollapsedPaths();
 1677  0 for (String s : paths) { builder.addCollapsedPath(s); }
 1678    }
 1679   
 1680  1 Iterable<AbsRelFile> exCp = getExtraClassPath();
 1681  1 if (exCp != null) {
 1682  0 for (AbsRelFile f : exCp) { builder.addClassPathFile(f); }
 1683    }
 1684    // else System.err.println("Project ClasspathVector is null!");
 1685   
 1686    // add build directory
 1687  1 File bd = getBuildDirectory();
 1688  0 if (bd != FileOps.NULL_FILE) builder.setBuildDirectory(bd);
 1689   
 1690    // add working directory
 1691  1 File wd = getWorkingDirectory(); // the value of the working directory to be stored in the project
 1692  1 if (wd != FileOps.NULL_FILE) builder.setWorkingDirectory(wd);
 1693   
 1694    // add jar main class
 1695  1 String mainClass = getMainClass();
 1696  0 if (mainClass != null) builder.setMainClass(mainClass);
 1697   
 1698    // add create jar file
 1699  1 File createJarFile = getCreateJarFile();
 1700  1 if (createJarFile != null) builder.setCreateJarFile(createJarFile);
 1701   
 1702  1 int createJarFlags = getCreateJarFlags();
 1703  0 if (createJarFlags != 0) builder.setCreateJarFlags (createJarFlags);
 1704   
 1705    // add breakpoints and watches
 1706  1 ArrayList<DebugBreakpointData> l = new ArrayList<DebugBreakpointData>();
 1707  1 for (OpenDefinitionsDocument odd: _breakpointManager.getDocuments()) {
 1708  0 for(Breakpoint bp: _breakpointManager.getRegions(odd)) { l.add(bp); }
 1709    }
 1710  1 builder.setBreakpoints(l);
 1711  1 try { builder.setWatches(getDebugger().getWatches()); }
 1712    catch(DebugException de) { /* ignore, just don't store watches */ }
 1713   
 1714    // add bookmarks
 1715  1 builder.setBookmarks(_bookmarkManager.getFileRegions());
 1716   
 1717  1 builder.setAutoRefreshStatus(_state.getAutoRefreshStatus());
 1718   
 1719    //add excluded files
 1720  0 for(File f: _state.getExclFiles()) { builder.addExcludedFile(f); }
 1721   
 1722    //add custom manifest
 1723  1 builder.setCustomManifest(_state.getCustomManifest());
 1724   
 1725    // update preference values here
 1726  1 Map<OptionParser<?>,String> sp = _state.getPreferencesStoredInProject();
 1727  1 for(OptionParser<?> key: sp.keySet()) {
 1728  0 sp.put(key, DrJava.getConfig().getOptionMap().getString(key));
 1729    }
 1730  1 builder.setPreferencesStoredInProject(sp);
 1731  1 _state.setPreferencesStoredInProject(sp);
 1732   
 1733  1 return builder;
 1734    }
 1735   
 1736    /** Writes the project profile augmented by usage info to specified file. Assumes DrJava is in project mode.
 1737    * @param file where to save the project
 1738    */
 1739  1 public void saveProject(File file, HashMap<OpenDefinitionsDocument, DocumentInfoGetter> info) throws IOException {
 1740    // if file is read-only, ask if it should be made writable
 1741  1 if (file.exists() && !file.canWrite()) {
 1742  0 File[] res = _notifier.filesReadOnly(new File[] {file});
 1743  0 for(File roFile: res) {
 1744  0 FileOps.makeWritable(roFile);
 1745    }
 1746  0 if (res.length == 0) { return; /* read-only, do not overwrite */ }
 1747    }
 1748   
 1749  1 ProjectProfile builder = _makeProjectProfile(file, info);
 1750    // write to disk
 1751  1 builder.write();
 1752   
 1753    // synchronized(_auxiliaryFiles) {
 1754    // _auxiliaryFiles = new LinkedList<File>();
 1755    // for (File f: builder.getAuxiliaryFiles()) { _auxiliaryFiles.add(f); }
 1756    // }
 1757   
 1758  1 setFileGroupingState(makeProjectFileGroupingState(builder.getProjectRoot(), builder.getMainClass(),
 1759    builder.getBuildDirectory(), builder.getWorkingDirectory(), file,
 1760    builder.getSourceFiles(), builder.getAuxiliaryFiles(),
 1761    builder.getExcludedFiles(),
 1762    builder.getClassPaths(), builder.getCreateJarFile(),
 1763    builder.getCreateJarFlags(), builder.getAutoRefreshStatus(),
 1764    builder.getCustomManifest(),
 1765    builder.getPreferencesStoredInProject()));
 1766    }
 1767   
 1768    /** Writes the project profile in the old project format. Assumes DrJava is in project mode.
 1769    * @param file where to save the project
 1770    */
 1771  0 public void exportOldProject(File file, HashMap<OpenDefinitionsDocument,DocumentInfoGetter> info) throws IOException {
 1772  0 ProjectProfile builder = _makeProjectProfile(file, info);
 1773   
 1774    // write to disk
 1775  0 builder.writeOld();
 1776   
 1777    // synchronized(_auxiliaryFiles) {
 1778    // _auxiliaryFiles = new LinkedList<File>();
 1779    // for (File f: builder.getAuxiliaryFiles()) { _auxiliaryFiles.add(f); }
 1780    // }
 1781   
 1782  0 setFileGroupingState(makeProjectFileGroupingState(builder.getProjectRoot(), builder.getMainClass (),
 1783    builder.getBuildDirectory(), builder.getWorkingDirectory(), file,
 1784    builder.getSourceFiles(), builder.getAuxiliaryFiles(),
 1785    builder.getExcludedFiles(),
 1786    builder.getClassPaths(), builder.getCreateJarFile(),
 1787    builder.getCreateJarFlags(), builder.getAutoRefreshStatus(),
 1788    builder.getCustomManifest(),
 1789    builder.getPreferencesStoredInProject()));
 1790    }
 1791   
 1792  0 public void reloadProject(File file, HashMap<OpenDefinitionsDocument, DocumentInfoGetter> info) throws IOException {
 1793  0 boolean projChanged = isProjectChanged();
 1794  0 ProjectProfile builder = _makeProjectProfile(file, info);
 1795  0 _loadProject(builder);
 1796  0 setProjectChanged(projChanged);
 1797    }
 1798   
 1799    /** Parses the given project file and loads it into the document navigator and resets interactions pane. Assumes
 1800    * preceding project, if any, has already been closed.
 1801    *
 1802    * @param projectFile The project file to parse