KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > AbstractGlobalModel


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32 END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model;
35
36 import java.awt.Color JavaDoc;
37 import java.awt.Container JavaDoc;
38 import java.awt.EventQueue JavaDoc;
39 import java.awt.Font JavaDoc;
40 import java.awt.event.FocusEvent JavaDoc;
41 import java.awt.event.FocusListener JavaDoc;
42 import java.awt.print.PageFormat JavaDoc;
43 import java.awt.print.Pageable JavaDoc;
44 import java.awt.print.PrinterException JavaDoc;
45 import java.awt.print.PrinterJob JavaDoc;
46 import java.io.ByteArrayInputStream JavaDoc;
47 import java.io.File JavaDoc;
48 import java.io.FileFilter JavaDoc;
49 import java.io.FileNotFoundException JavaDoc;
50 import java.io.FileReader JavaDoc;
51 import java.io.FilenameFilter JavaDoc;
52 import java.io.InputStreamReader JavaDoc;
53 import java.io.IOException JavaDoc;
54 import java.io.OutputStream JavaDoc;
55 import java.io.StringReader JavaDoc;
56
57 import java.util.AbstractMap JavaDoc;
58 import java.util.ArrayList JavaDoc;
59 import java.util.Collections JavaDoc;
60 import java.util.Comparator JavaDoc;
61 import java.util.HashSet JavaDoc;
62 import java.util.Hashtable JavaDoc;
63 import java.util.LinkedHashMap JavaDoc;
64 import java.util.LinkedList JavaDoc;
65 import java.util.List JavaDoc;
66 import java.util.ListIterator JavaDoc;
67 import java.util.Vector JavaDoc;
68 import java.util.WeakHashMap JavaDoc;
69
70 import javax.swing.SwingUtilities JavaDoc;
71 import javax.swing.event.DocumentListener JavaDoc;
72 import javax.swing.event.UndoableEditListener JavaDoc;
73 import javax.swing.text.AttributeSet JavaDoc;
74 import javax.swing.text.BadLocationException JavaDoc ;
75 import javax.swing.text.Element JavaDoc;
76 import javax.swing.text.Position JavaDoc;
77 import javax.swing.text.Segment JavaDoc;
78 import javax.swing.text.Style JavaDoc;
79 import javax.swing.ProgressMonitor JavaDoc;
80
81 import edu.rice.cs.drjava.DrJava;
82 import edu.rice.cs.drjava.DrJavaRoot;
83 import edu.rice.cs.drjava.config.FileOption;
84 import edu.rice.cs.drjava.config.OptionConstants;
85 import edu.rice.cs.drjava.config.OptionEvent ;
86 import edu.rice.cs.drjava.config.OptionListener;
87 import edu.rice.cs.drjava.model.cache.DCacheAdapter;
88 import edu.rice.cs.drjava.model.cache.DDReconstructor;
89 import edu.rice.cs.drjava.model.cache.DocumentCache ;
90 import edu.rice.cs.drjava.model.compiler.CompilerModel;
91 import edu.rice.cs.drjava.model.debug.Breakpoint;
92 import edu.rice.cs.drjava.model.debug.DebugBreakpointData;
93 import edu.rice.cs.drjava.model.debug.DebugException ;
94 import edu.rice.cs.drjava.model.debug.DebugWatchData;
95 import edu.rice.cs.drjava.model.debug.Debugger;
96 import edu.rice.cs.drjava.model.debug.NoDebuggerAvailable;
97 import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException ;
98 import edu.rice.cs.drjava.model.definitions.CompoundUndoManager;
99 import edu.rice.cs.drjava.model.definitions.DefinitionsDocument;
100 //import edu.rice.cs.drjava.model.definitions.DefinitionsDocument.WrappedPosition;
101
import edu.rice.cs.drjava.model.definitions.DefinitionsEditorKit;
102 import edu.rice.cs.drjava.model.definitions.DocumentUIListener ;
103 import edu.rice.cs.drjava.model.definitions.InvalidPackageException;
104 import edu.rice.cs.drjava.model.definitions.reducedmodel.HighlightStatus;
105 import edu.rice.cs.drjava.model.definitions.reducedmodel.IndentInfo ;
106 import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelState;
107 import edu.rice.cs.drjava.model.junit.JUnitModel;
108 import edu.rice.cs.drjava.model.print.DrJavaBook;
109 import edu.rice.cs.drjava.model.repl.DefaultInteractionsModel ;
110 import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
111 import edu.rice.cs.drjava.model.repl.InteractionsDocument;
112 import edu.rice.cs.drjava.model.repl.InteractionsScriptModel;
113 import edu.rice.cs.drjava.project.DocFile ;
114 import edu.rice.cs.drjava.project.DocumentInfoGetter;
115 import edu.rice.cs.drjava.project.MalformedProjectFileException;
116 import edu.rice.cs.drjava.project.ProjectFileIR;
117 import edu.rice.cs.drjava.project.ProjectFileParser ;
118 import edu.rice.cs.drjava.project.ProjectProfile;
119 import edu.rice.cs.drjava.ui.MainFrame;
120 import edu.rice.cs.drjava.ui.SplashScreen;
121
122 import edu.rice.cs.plt.tuple.Pair;
123 import edu.rice.cs.plt.io.IOUtil;
124 import edu.rice.cs.plt.iter.IterUtil;
125 import edu.rice.cs.plt.lambda.LambdaUtil;
126
127 import edu.rice.cs.util.ClassPathVector;
128 import edu.rice.cs.util.FileOpenSelector;
129 import edu.rice.cs.util.FileOps;
130 import edu.rice.cs.util.Lambda;
131 import edu.rice.cs.util.Log;
132 import edu.rice.cs.util.NullFile;
133 import edu.rice.cs.util.OperationCanceledException ;
134 import edu.rice.cs.util.OrderedHashSet;
135 import edu.rice.cs.util.SRunnable;
136 import edu.rice.cs.util.StringOps;
137 import edu.rice.cs.util.UnexpectedException;
138 import edu.rice.cs.util.docnavigation.AWTContainerNavigatorFactory ;
139 import edu.rice.cs.util.docnavigation.IDocumentNavigator;
140 import edu.rice.cs.util.docnavigation.INavigationListener;
141 import edu.rice.cs.util.docnavigation.INavigatorItem;
142 import edu.rice.cs.util.docnavigation.INavigatorItemFilter ;
143 import edu.rice.cs.util.docnavigation.JTreeSortNavigator;
144 import edu.rice.cs.util.docnavigation.NodeData;
145 import edu.rice.cs.util.docnavigation.NodeDataVisitor;
146 import edu.rice.cs.util.swing.AsyncCompletionArgs ;
147 import edu.rice.cs.util.swing.AsyncTask;
148 import edu.rice.cs.util.swing.IAsyncProgress;
149 import edu.rice.cs.util.swing.DocumentIterator;
150 import edu.rice.cs.util.swing.Utilities;
151 import edu.rice.cs.util.text.AbstractDocumentInterface ;
152 import edu.rice.cs.util.text.ConsoleDocument;
153 import edu.rice.cs.util.ReaderWriterLock;
154
155 import static java.lang.Math JavaDoc.*;
156
157 /** In simple terms, a DefaultGlobalModel without an interpreter, compiler, junit testing, debugger or javadoc.
158   * Hence, it only has only document handling functionality
159   * @version $Id: AbstractGlobalModel.java 4088 2007-01-25 16:52:24Z dlsmith $
160   */

161 public class AbstractGlobalModel implements SingleDisplayModel, OptionConstants, DocumentIterator {
162   
163   public static Log _log = new Log("GlobalModel.txt", false);
164  
165   /** A document cache that manages how many unmodified documents are open at once. */
166   protected DocumentCache _cache;
167  
168   static final String JavaDoc DOCUMENT_OUT_OF_SYNC_MSG =
169     "Current document is out of sync with the Interactions Pane and should be recompiled!\n";
170  
171   static final String JavaDoc CLASSPATH_OUT_OF_SYNC_MSG =
172     "Interactions Pane is out of sync with the current classpath and should be reset!\n";
173  
174   // ----- FIELDS -----
175

176 // /** A list of files that are auxiliary files to the currently open project. All accesses to this variable must be synchronized.
177
// * TODO: make part of FileGroupingState. */
178
// private LinkedList<File> _auxiliaryFiles = new LinkedList<File>();
179

180   /** Adds a document to the list of auxiliary files. The LinkedList class is not thread safe, so
181    * the add operation is synchronized.
182    */

183   public void addAuxiliaryFile(OpenDefinitionsDocument doc) { _state.addAuxFile(doc.getRawFile()); }
184 // if (! doc.inProject()) {
185
// File f;
186
//
187
// try { f = doc.getFile(); }
188
// catch(FileMovedException fme) { f = fme.getFile(); }
189
//
190
// synchronized(_auxiliaryFiles) { _auxiliaryFiles.add(f); }
191
// setProjectChanged(true);
192
// }
193
// }
194

195   /** Removes a document from the list of auxiliary files. The LinkedList class is not thread safe, so
196    * operations on _auxiliaryFiles are synchronized.
197    */

198   public void removeAuxiliaryFile(OpenDefinitionsDocument doc) { _state.remAuxFile(doc.getRawFile()); }
199 // File file = doc.getRawFile();
200
// if (file == null) return; // Should never happen unless doc is Untitled.
201
// String path = FileOps.getCanonicalPath(file);
202
//
203
// synchronized(_auxiliaryFiles) {
204
// ListIterator<File> it = _auxiliaryFiles.listIterator();
205
// while (it.hasNext()) {
206
// if (path.equals (FileOps.getCanonicalPath(it.next()))) {
207
// it.remove();
208
// setProjectChanged(true);
209
// break;
210
// }
211
// }
212
// }
213
// }
214

215   /** Keeps track of all listeners to the model, and has the ability to notify them of some event. Originally used
216    * a Command Pattern style, but this has been replaced by having EventNotifier directly implement all listener
217    * interfaces it supports. Set in constructor so that subclasses can install their own notifier with additional
218    * methods.
219    */

220   public final GlobalEventNotifier _notifier = new GlobalEventNotifier();
221  
222   // ---- Definitions fields ----
223

224   /** Factory for new definitions documents and views.*/
225   protected final DefinitionsEditorKit _editorKit = new DefinitionsEditorKit(_notifier);
226  
227   /** Collection for storing all OpenDefinitionsDocuments. */
228 // protected final OrderedHashSet<OpenDefinitionsDocument>
229
private final AbstractMap JavaDoc<File JavaDoc, OpenDefinitionsDocument> _documentsRepos =
230     new LinkedHashMap JavaDoc<File JavaDoc, OpenDefinitionsDocument>();
231  
232   // ---- Input/Output Document Fields ----
233

234   /** The document used to display System.out and System.err, and to read from System.in. */
235   protected final ConsoleDocument _consoleDoc;
236  
237   /** The document adapter used in the console document. */
238   protected final InteractionsDJDocument _consoleDocAdapter;
239  
240   /** A lock object to prevent print calls to System.out or System.err from flooding the JVM, ensuring the UI
241    * remains responsive.
242    */

243   private final Object JavaDoc _systemWriterLock = new Object JavaDoc();
244  
245   /** Number of milliseconds to wait after each println, to prevent the JVM from being flooded with print calls.
246    * TODO: why is this here, and why is it public?
247    */

248   public static final int WRITE_DELAY = 5;
249  
250   /** A PageFormat object for printing to paper. */
251   protected volatile PageFormat JavaDoc _pageFormat = new PageFormat JavaDoc();
252  
253   /** The active document pointer, which will never be null once the constructor is done.
254    * Maintained by the _gainVisitor with a navigation listener.
255    */

256   private volatile OpenDefinitionsDocument _activeDocument;
257  
258   /** A pointer to the active directory, which is not necessarily the parent of the active document
259    * The user may click on a folder component in the navigation pane and that will set this field without
260    * setting the active document. It is used by the newFile method to place new files into the active directory.
261    */

262   private volatile File JavaDoc _activeDirectory;
263   
264   /** A state varible indicating whether the class path has changed. Reset to false by resetInteractions. */
265   private volatile boolean classPathChanged = false;
266    
267   /** The abstract container which contains views of open documents and allows user to navigate document focus among
268    * this collection of open documents
269    */

270   protected volatile IDocumentNavigator<OpenDefinitionsDocument> _documentNavigator =
271       new AWTContainerNavigatorFactory<OpenDefinitionsDocument>().makeListNavigator();
272
273   /** Manager for breakpoint regions. */
274   protected final ConcreteRegionManager<Breakpoint> _breakpointManager;
275  
276   /** @return manager for breakpoint regions. */
277   public RegionManager<Breakpoint> getBreakpointManager() { return _breakpointManager; }
278  
279   /** Manager for bookmark regions. */
280   protected final ConcreteRegionManager<DocumentRegion> _bookmarkManager;
281  
282   /** @return manager for bookmark regions. */
283   public RegionManager<DocumentRegion> getBookmarkManager() { return _bookmarkManager; }
284  
285   /** Managers for find result regions. */
286   protected final LinkedList JavaDoc<RegionManager<MovingDocumentRegion>> _findResultsManagers;
287  
288   /** @return manager for find result regions. */
289   public List JavaDoc<RegionManager<MovingDocumentRegion>> getFindResultsManagers() {
290     return new LinkedList JavaDoc<RegionManager<MovingDocumentRegion>>(_findResultsManagers);
291   }
292  
293   /** @return new manager for find result regions. */
294   public RegionManager<MovingDocumentRegion> createFindResultsManager() {
295     ConcreteRegionManager<MovingDocumentRegion> rm = new ConcreteRegionManager<MovingDocumentRegion>();
296     _findResultsManagers.add(rm);
297     
298     // install new manager in all documents
299
for (final OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
300       doc.addFindResultsManager(rm);
301     }
302     return rm;
303   }
304  
305   /** Dispose a manager for find result regions. */
306   public void disposeFindResultsManager(RegionManager<MovingDocumentRegion> rm) {
307     // remove manager from all documents
308
for (final OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
309       doc.removeFindResultsManager(rm);
310     }
311     _findResultsManagers.remove(rm);
312   }
313  
314   /** Manager for browser history regions. */
315   protected final ConcreteRegionManager<DocumentRegion> _browserHistoryManager;
316   
317   /** @return manager for browser history regions. */
318   public RegionManager<DocumentRegion> getBrowserHistoryManager() { return _browserHistoryManager; }
319   
320 // Any lightweight parsing has been disabled until we have something that is beneficial and works better in the background.
321
// /** Light-weight parsing controller. */
322
// protected LightWeightParsingControl _parsingControl;
323
//
324
// /** @return the parsing control */
325
// public LightWeightParsingControl getParsingControl() { return _parsingControl; }
326

327   // ----- CONSTRUCTORS -----
328

329   /** Constructs a new GlobalModel. Creates a new MainJVM and starts its Interpreter JVM. */
330   public AbstractGlobalModel() {
331     _cache = new DocumentCache();
332     
333     _consoleDocAdapter = new InteractionsDJDocument();
334     _consoleDoc = new ConsoleDocument(_consoleDocAdapter);
335     
336     _bookmarkManager = new ConcreteRegionManager<DocumentRegion>();
337     _findResultsManagers = new LinkedList JavaDoc<RegionManager<MovingDocumentRegion>>();
338     _browserHistoryManager = new ConcreteRegionManager<DocumentRegion>();
339     
340     _breakpointManager = new ConcreteRegionManager<Breakpoint>() {
341       public boolean changeRegionHelper(final Breakpoint oldBP, final Breakpoint newBP) {
342         // override helper so the enabled flag is copied
343
if (oldBP.isEnabled() != newBP.isEnabled()) {
344           oldBP.setEnabled(newBP.isEnabled());
345           return true;
346         }
347         return false;
348       }
349     };
350     
351     _registerOptionListeners();
352         
353     setFileGroupingState(makeFlatFileGroupingState());
354     _notifier.projectRunnableChanged();
355     _init();
356   }
357  
358   private void _init() {
359
360     /** This visitor is invoked by the DocumentNavigator to update _activeDocument among other things */
361     final NodeDataVisitor<OpenDefinitionsDocument, Boolean JavaDoc> _gainVisitor = new NodeDataVisitor<OpenDefinitionsDocument, Boolean JavaDoc>() {
362       public Boolean JavaDoc itemCase(OpenDefinitionsDocument doc, Object JavaDoc... p) {
363         Boolean JavaDoc modelInitiated = (Boolean JavaDoc)p[0];
364 // if (!modelInitiated) {
365
// IDocumentNavigator.LOG.log("_gainVisitor; modelInitiated = false");
366
// }
367
OpenDefinitionsDocument oldDoc = AbstractGlobalModel.this.getActiveDocument();
368 // if (! modelInitiated) { addToBrowserHistory(); }
369
_setActiveDoc(doc); // sets _activeDocument, the shadow copy of the active document
370
// if (! modelInitiated) { addToBrowserHistory(); }
371

372 // Utilities.showDebug("Setting the active doc done");
373
final File JavaDoc oldDir = _activeDirectory; // _activeDirectory can be null
374
final File JavaDoc dir = doc.getParentDirectory(); // dir can be null
375
if (dir != null && ! dir.equals(oldDir)) {
376         /* If the file is in External or Auxiliary Files then then we do not want to change our project directory
377          * to something outside the project. ?? */

378           _activeDirectory = dir;
379           _notifier.currentDirectoryChanged(_activeDirectory);
380         }
381         return Boolean.valueOf(true);
382       }
383       public Boolean JavaDoc fileCase(File JavaDoc f, Object JavaDoc... p) {
384         if (! f.isAbsolute()) { // should never happen because all file names are canonicalized
385
File JavaDoc root = _state.getProjectFile().getParentFile().getAbsoluteFile();
386           f = new File JavaDoc(root, f.getPath());
387         }
388         _activeDirectory = f; // Invariant: activeDirectory != null
389
_notifier.currentDirectoryChanged(f);
390         return Boolean.valueOf(true);
391       }
392       public Boolean JavaDoc stringCase(String JavaDoc s, Object JavaDoc... p) { return Boolean.valueOf(false); }
393     };
394     
395     _documentNavigator.addNavigationListener(new INavigationListener<OpenDefinitionsDocument>() {
396       public void gainedSelection(NodeData<? extends OpenDefinitionsDocument> dat, boolean modelInitiated) {
397         dat.execute(_gainVisitor, modelInitiated); }
398       public void lostSelection(NodeData<? extends OpenDefinitionsDocument> dat, boolean modelInitiated) {
399         /* not important, only one document selected at a time */ }
400     });
401     
402     _documentNavigator.addFocusListener(new FocusListener JavaDoc() {
403       public void focusGained(FocusEvent JavaDoc e) {
404 // Utilities.show("focusGained called with event " + e);
405
if (_documentNavigator.getCurrent() != null) // past selection is leaf node
406
_notifier.focusOnDefinitionsPane();
407       }
408       public void focusLost(FocusEvent JavaDoc e) { }
409     });
410     
411     _ensureNotEmpty();
412     setActiveFirstDocument();
413     
414     // setup option listener for clipboard history
415
OptionListener<Integer JavaDoc> clipboardHistorySizeListener = new OptionListener<Integer JavaDoc>() {
416       public void optionChanged(OptionEvent<Integer JavaDoc> oce) {
417         ClipboardHistoryModel.singleton().resize(oce.value);
418       }
419     };
420     DrJava.getConfig().addOptionListener(CLIPBOARD_HISTORY_SIZE, clipboardHistorySizeListener);
421     ClipboardHistoryModel.singleton().resize(DrJava.getConfig().getSetting(CLIPBOARD_HISTORY_SIZE).intValue());
422     
423     // setup option listener for browser history
424
OptionListener<Integer JavaDoc> browserHistoryMaxSizeListener = new OptionListener<Integer JavaDoc>() {
425       public void optionChanged(OptionEvent<Integer JavaDoc> oce) {
426         AbstractGlobalModel.this.getBrowserHistoryManager().setMaximumSize(oce.value);
427       }
428     };
429     DrJava.getConfig().addOptionListener(BROWSER_HISTORY_MAX_SIZE, browserHistoryMaxSizeListener);
430     getBrowserHistoryManager().setMaximumSize(DrJava.getConfig().getSetting(BROWSER_HISTORY_MAX_SIZE).intValue());
431   }
432  
433   /** Returns a source root given a package and filename. */
434   protected File JavaDoc getSourceRoot(String JavaDoc packageName, File JavaDoc sourceFile) throws InvalidPackageException {
435 // Utilities.show("getSourceRoot(" + packageName + ", " + sourceFile + " called");
436
if (packageName.equals("")) {
437 // Utilities.show("Source root of " + sourceFile + " is: " + sourceFile.getParentFile());
438
return sourceFile.getParentFile();
439     }
440     
441     ArrayList JavaDoc<String JavaDoc> packageStack = new ArrayList JavaDoc<String JavaDoc>();
442     int dotIndex = packageName.indexOf('.');
443     int curPartBegins = 0;
444     
445     while (dotIndex != -1) {
446       packageStack.add (packageName.substring(curPartBegins, dotIndex));
447       curPartBegins = dotIndex + 1;
448       dotIndex = packageName.indexOf('.', dotIndex + 1);
449     }
450     
451     // Now add the last package component
452
packageStack.add (packageName.substring(curPartBegins));
453     
454     // Must use the canonical path, in case there are dots in the path
455
// (which will conflict with the package name)
456
try {
457       File JavaDoc parentDir = sourceFile.getCanonicalFile ();
458       while (! packageStack.isEmpty()) {
459         String JavaDoc part = pop(packageStack);
460         parentDir = parentDir.getParentFile();
461         if (parentDir == null) throw new UnexpectedException("parent dir is null!");
462         
463         // Make sure the package piece matches the directory name
464
if (! part.equals(parentDir.getName())) {
465           String JavaDoc msg = "The source file " + sourceFile.getAbsolutePath() +
466             " is in the wrong directory or in the wrong package. " +
467             "The directory name " + parentDir.getName() +
468             " does not match the package component " + part + ".";
469           
470           throw new InvalidPackageException(-1, msg);
471         }
472       }
473       
474       // OK, now parentDir points to the directory of the first component of the
475
// package name. The parent of that is the root.
476
parentDir = parentDir.getParentFile();
477       if (parentDir == null) {
478         throw new RuntimeException JavaDoc("parent dir of first component of package name is null!");
479       }
480       return parentDir;
481     }
482     catch (IOException JavaDoc ioe) {
483       String JavaDoc msg = "Could not locate directory of the source file: " + ioe;
484       throw new InvalidPackageException(-1, msg);
485     }
486   }
487  
488   // ----- STATE -----
489

490   /** Specifies the state of the navigator pane. The global model delegates the compileAll command to the _state, a
491     * FileGroupingState.nSynchronization is handled by the compilerModel.
492     */

493   protected volatile FileGroupingState _state;
494    
495   /** @param state the new file grouping state. */
496   public void setFileGroupingState(FileGroupingState state) {
497     _state = state;
498     _notifier.projectRunnableChanged();
499     _notifier.projectBuildDirChanged();
500     _notifier.projectWorkDirChanged();
501 // _notifier.projectModified(); // not currently used
502
}
503  
504   protected FileGroupingState
505     makeProjectFileGroupingState(File JavaDoc pr, File JavaDoc main, File JavaDoc bd, File JavaDoc wd, File JavaDoc project, File JavaDoc[] srcFiles, File JavaDoc[] auxFiles,
506                                  ClassPathVector cp, File JavaDoc cjf, int cjflags) {
507     return new ProjectFileGroupingState(pr, main, bd, wd, project, srcFiles, auxFiles, cp, cjf, cjflags);
508   }
509  
510   /** @return true if the class path state has been changed. */
511   public boolean isClassPathChanged() { return classPathChanged; }
512   
513   /** Updates the classpath state. */
514   public void setClassPathChanged(boolean changed) {
515     classPathChanged = changed;
516   }
517   
518   /** Notifies the project state that the project has been changed. */
519   public void setProjectChanged(boolean changed) {
520     _state.setProjectChanged(changed);
521 // _notifier.projectModified(); // not currently used
522
}
523    
524   /** @return true if the project state has been changed. */
525   public boolean isProjectChanged() { return _state.isProjectChanged(); }
526  
527   /** @return true if the model has a project open, false otherwise. */
528   public boolean isProjectActive() { return _state.isProjectActive(); }
529  
530   /** @return the file that points to the current project file. Null if not currently in project view
531    */

532   public File JavaDoc getProjectFile() { return _state.getProjectFile(); }
533  
534   /** @return all files currently saved as source files in the project file.
535    * If _state not in project mode, returns null
536    */

537   public File JavaDoc[] getProjectFiles() { return _state.getProjectFiles(); }
538  
539   /** @return true the given file is in the current project file. */
540   public boolean inProject(File JavaDoc f) { return _state.inProject(f); }
541  
542   /** A file is in the project if the source root is the same as the
543    * project root. this means that project files must be saved at the
544    * source root. (we query the model through the model's state)
545    */

546   public boolean inProjectPath(OpenDefinitionsDocument doc) { return _state.inProjectPath(doc); }
547  
548   /** Sets the class with the project's main method. */
549   public void setMainClass(File JavaDoc f) {
550     _state.setMainClass(f);
551     _notifier.projectRunnableChanged();
552     setProjectChanged(true);
553   }
554  
555   /** @return the class with the project's main method. */
556   public File JavaDoc getMainClass() { return _state.getMainClass(); }
557  
558   /** Sets the create jar file of the project. */
559   public void setCreateJarFile(File JavaDoc f) {
560     _state.setCreateJarFile(f);
561     setProjectChanged(true);
562   }
563  
564     /** Return the create jar file for the project. If not in project mode, returns null. */
565   public File JavaDoc getCreateJarFile() { return _state.getCreateJarFile(); }
566  
567   /** Sets the create jar flags of the project. */
568   public void setCreateJarFlags(int f) {
569     _state.setCreateJarFlags(f);
570     setProjectChanged(true);
571   }
572  
573     /** Return the create jar flags for the project. If not in project mode, returns 0. */
574   public int getCreateJarFlags() { return _state.getCreateJarFlags(); }
575  
576    /** @return the root of the project sourc tree (assuming one exists). */
577   public File JavaDoc getProjectRoot() { return _state.getProjectRoot(); }
578     
579   /** Sets the class with the project's main method. Degenerate version overridden in DefaultGlobalModel. */
580   public void setProjectRoot(File JavaDoc f) {
581     _state.setProjectRoot(f);
582 // _notifier.projectRootChanged();
583
setProjectChanged(true);
584   }
585  
586   /** Sets project file to specifed value; used in "Save Project As ..." command in MainFrame. */
587   public void setProjectFile(File JavaDoc f) { _state.setProjectFile(f); }
588  
589   /** @return the build directory for the project (assuming one exists). */
590   public File JavaDoc getBuildDirectory() { return _state.getBuildDirectory(); }
591  
592   /** Sets the class with the project's main method. Degenerate version overridden in DefaultGlobalModel. */
593   public void setBuildDirectory(File JavaDoc f) {
594     _state.setBuildDirectory(f);
595     _notifier.projectBuildDirChanged();
596     setProjectChanged(true);
597   }
598  
599   /** @return the working directory for the Master JVM (editor and GUI). */
600   public File JavaDoc getMasterWorkingDirectory() {
601     File JavaDoc file;
602     try {
603       // restore the path from the configuration
604
file = FileOps.getValidDirectory(DrJava.getConfig().getSetting(LAST_DIRECTORY));
605     }
606     catch (RuntimeException JavaDoc e) {
607       // something went wrong, clear the setting and use "user.home"
608
DrJava.getConfig().setSetting(LAST_DIRECTORY, FileOption.NULL_FILE);
609       file = FileOps.getValidDirectory(DrJava.getConfig().getSetting(LAST_DIRECTORY));
610     }
611     // update the setting and return it
612
DrJava.getConfig().setSetting(LAST_DIRECTORY, file);
613     return file;
614   }
615       
616   /** @return the working directory for the Slave (Interactions) JVM */
617   public File JavaDoc getWorkingDirectory() { return _state.getWorkingDirectory(); }
618  
619   /** Sets the working directory for the project; ignored in flat file model. */
620   public void setWorkingDirectory(File JavaDoc f) {
621     _state.setWorkingDirectory(f);
622     _notifier.projectWorkDirChanged();
623     setProjectChanged(true);
624     if (DrJava.getConfig().getSetting(STICKY_INTERACTIONS_DIRECTORY)) {
625       // update the setting
626
DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, _state.getWorkingDirectory());
627     }
628   }
629  
630   public void cleanBuildDirectory() { _state.cleanBuildDirectory(); }
631  
632   public List JavaDoc<File JavaDoc> getClassFiles() { return _state.getClassFiles(); }
633  
634   /** Helper method used in subsequent anonymous inner class */
635   protected static String JavaDoc getPackageName(String JavaDoc classname) {
636     int index = classname.lastIndexOf(".");
637     if (index != -1) return classname.substring(0, index);
638     else return "";
639   }
640  
641  
642   class ProjectFileGroupingState implements FileGroupingState {
643     
644     volatile File JavaDoc _projRoot;
645     volatile File JavaDoc _mainFile;
646     volatile File JavaDoc _buildDir;
647     volatile File JavaDoc _workDir;
648     volatile File JavaDoc _projectFile;
649     final File JavaDoc[] _projectFiles;
650     volatile File JavaDoc[] _auxFiles;
651     volatile ClassPathVector _projExtraClassPath;
652     private boolean _isProjectChanged = false;
653     volatile File JavaDoc _createJarFile;
654     volatile int _createJarFlags;
655     
656     HashSet JavaDoc<String JavaDoc> _projFilePaths = new HashSet JavaDoc<String JavaDoc>();
657     
658     /** Degenerate constructor for a new project; only the file project name is known. */
659     ProjectFileGroupingState(File JavaDoc project) {
660       this(project.getParentFile(), null, null, null, project, new File JavaDoc[0], new File JavaDoc[0], new ClassPathVector(), null, 0);
661     }
662     
663     ProjectFileGroupingState(File JavaDoc pr, File JavaDoc main, File JavaDoc bd, File JavaDoc wd, File JavaDoc project, File JavaDoc[] srcFiles, File JavaDoc[] auxFiles, ClassPathVector cp, File JavaDoc cjf, int cjflags) {
664       _projRoot = pr;
665 // System.err.println("Project root initialized to " + pr);
666
_mainFile = main;
667       _buildDir = bd;
668       _workDir = wd;
669       _projectFile = project;
670       _projectFiles = srcFiles;
671       _auxFiles = auxFiles;
672       _projExtraClassPath = cp;
673       
674       if (_projectFiles != null) try { for (File JavaDoc file : _projectFiles) { _projFilePaths.add( file.getCanonicalPath()); } }
675       catch(IOException JavaDoc e) { /*do nothing */ }
676       
677       _createJarFile = cjf;
678       _createJarFlags = cjflags;
679     }
680     
681     public boolean isProjectActive() { return true; }
682     
683     /** Determines whether the specified doc in within the project file tree.
684      * No synchronization is required because only immutable data is accessed.
685      */

686     public boolean inProjectPath(OpenDefinitionsDocument doc) {
687       if (doc.isUntitled()) return false;
688       
689       // If the file does not exist, we still want to tell if it's in the correct
690
// path. The file may have been in at one point and had been removed, in which
691
// case we should treat it as an untitled project file that should be resaved.
692
File JavaDoc f;
693       try { f = doc.getFile(); }
694       catch(FileMovedException fme) { f = fme.getFile(); }
695       return inProjectPath(f);
696     }
697     
698     /** Determines whether the specified file in within the project file tree.
699      * No synchronization is required because only immutable data is accessed.
700      */

701     public boolean inProjectPath(File JavaDoc f) { return IOUtil.isMember(f, getProjectRoot()); }
702     
703     /** @return the absolute path to the project file. Since projectFile is final, no synchronization
704      * is necessary.
705      */

706     public File JavaDoc getProjectFile() { return _projectFile; }
707     
708     public boolean inProject(File JavaDoc f) {
709       String JavaDoc path;
710       
711       if (isUntitled(f) || ! inProjectPath(f)) return false;
712       try {
713         path = f.getCanonicalPath();
714         return _projFilePaths.contains(path);
715       }
716       catch(IOException JavaDoc ioe) { return false; }
717     }
718     
719     public File JavaDoc[] getProjectFiles() { return _projectFiles; }
720     
721     public File JavaDoc getProjectRoot() {
722       if (_projRoot == null || _projRoot.equals( FileOption.NULL_FILE)) return _projectFile.getParentFile();
723 // Utilities.show("File grouping state returning project root of " + _projRoot);
724
return _projRoot;
725     }
726     
727     public File JavaDoc getBuildDirectory() { return _buildDir; }
728     
729     public File JavaDoc getWorkingDirectory() {
730       try {
731         if (_workDir == null || _workDir == FileOption.NULL_FILE)
732           return _projectFile.getParentFile().getCanonicalFile(); // default is project root
733
return _workDir.getCanonicalFile();
734       }
735       catch(IOException JavaDoc e) { /* fall through */ }
736       return _workDir.getAbsoluteFile();
737     }
738     
739     /** Sets project file to specifed value; used in "Save Project As ..." command in MainFrame. */
740     public void setProjectFile(File JavaDoc f) { _projectFile = f; }
741     
742     public void setProjectRoot(File JavaDoc f) {
743       _projRoot = f;
744 // System.err.println("Project root set to " + f);
745
}
746     
747     /** Adds File f to end of _auxFiles array. */
748     public void addAuxFile(File JavaDoc f) {
749       synchronized(_auxFiles) {
750         int n = _auxFiles.length;
751         File JavaDoc[] newAuxFiles = new File JavaDoc[n + 1];
752         System.arraycopy(_auxFiles, 0, newAuxFiles, 0, n); // newAuxFiles[0:n-1] = _auxFiles[0:n-1]
753
newAuxFiles[n] = f;
754         _auxFiles = newAuxFiles;
755       }
756     }
757     
758     /** Removes File f from _auxFiles array. Assumes that f is a member of _auxFiles. If f is not found, throws an
759      * UnexpectedException. */

760     public void remAuxFile(File JavaDoc file) {
761       synchronized(_auxFiles) {
762         int newLen = _auxFiles.length - 1;
763         File JavaDoc[] newAuxFiles = new File JavaDoc[newLen];
764         try {
765           int j = 0;
766           for (File JavaDoc f: _auxFiles) {
767             if (! f.equals(file)) {
768               newAuxFiles[j] = file;
769               j++;
770             }
771           }
772           if (j < newLen) throw new IllegalStateException JavaDoc("auxFiles list contain two copies of " + file);
773           _auxFiles = newAuxFiles;
774         }
775         catch(Exception JavaDoc e) { // negative array size or index out of bounds
776
throw new UnexpectedException(e);
777         }
778       }
779     }
780     
781     public void setBuildDirectory(File JavaDoc f) { _buildDir = f; }
782     
783     public void setWorkingDirectory(File JavaDoc f) { _workDir = f; }
784     
785     public File JavaDoc getMainClass() { return _mainFile; }
786     
787     public void setMainClass(File JavaDoc f) { _mainFile = f; }
788     
789     public void setCreateJarFile(File JavaDoc f) { _createJarFile = f; }
790  
791     public File JavaDoc getCreateJarFile() { return _createJarFile; }
792     
793     public void setCreateJarFlags(int f) { _createJarFlags = f; }
794  
795     public int getCreateJarFlags() { return _createJarFlags; }
796     
797     public boolean isProjectChanged() { return _isProjectChanged; }
798     
799     public void setProjectChanged(boolean changed) { _isProjectChanged = changed; }
800     
801     public boolean isAuxiliaryFile(File JavaDoc f) {
802       String JavaDoc path;
803       
804       if (isUntitled(f)) return false;
805       
806       try { path = f.getCanonicalPath();}
807       catch(IOException JavaDoc ioe) { return false; }
808       
809       synchronized(_auxFiles) {
810         for (File JavaDoc file : _auxFiles) {
811           try { if (file.getCanonicalPath().equals(path)) return true; }
812           catch(IOException JavaDoc ioe) { /* ignore file */ }
813         }
814         return false;
815       }
816     }
817     
818     // This only starts the process. It is all done asynchronously.
819
public void cleanBuildDirectory() {
820       File JavaDoc dir = this.getBuildDirectory ();
821       _notifier.executeAsyncTask(_findFilesToCleanTask, dir, false, true);
822     }
823     
824     private AsyncTask<File JavaDoc,List JavaDoc<File JavaDoc>> _findFilesToCleanTask = new AsyncTask<File JavaDoc,List JavaDoc<File JavaDoc>>("Find Files to Clean") {
825       private FilenameFilter JavaDoc _filter = new FilenameFilter JavaDoc() {
826         public boolean accept(File JavaDoc parent, String JavaDoc name) {
827           return new File JavaDoc(parent, name).isDirectory() || name.endsWith(".class");
828         }
829       };
830       
831       public List JavaDoc<File JavaDoc> runAsync(File JavaDoc buildDir, IAsyncProgress monitor) throws Exception JavaDoc {
832         List JavaDoc<File JavaDoc> accumulator = new LinkedList JavaDoc<File JavaDoc>();
833         helper(buildDir, accumulator); // adds files to the accumulator recursively
834
return accumulator;
835       }
836       public void complete(AsyncCompletionArgs<List JavaDoc<File JavaDoc>> args) {
837         _notifier.executeAsyncTask(_deleteFilesTask, args.getResult(), true, true);
838       }
839       public String JavaDoc getDiscriptionMessage() {
840         return "Finding files to delete...";
841       }
842       private void helper(File JavaDoc file, List JavaDoc<File JavaDoc> accumulator) {
843         if (file.isDirectory ()) {
844           File JavaDoc[] children = file.listFiles(_filter);
845           for (File JavaDoc child : children) {
846             helper(child, accumulator);
847             accumulator.add(file);
848           }
849         }
850         else if ( file.getName().endsWith(".class")){
851           accumulator.add(file);
852         }
853       }
854     };
855     
856     private AsyncTask<List JavaDoc<File JavaDoc>,List JavaDoc<File JavaDoc>> _deleteFilesTask = new AsyncTask<List JavaDoc<File JavaDoc>,List JavaDoc<File JavaDoc>>("Delete Files") {
857       public List JavaDoc<File JavaDoc> runAsync(List JavaDoc<File JavaDoc> filesToDelete, IAsyncProgress monitor) throws Exception JavaDoc {
858         List JavaDoc<File JavaDoc> undeletableFiles = new ArrayList JavaDoc<File JavaDoc>();
859         
860         monitor.setMinimum (0);
861         monitor.setMaximum(filesToDelete.size());
862         int progress = 1;
863         for(File JavaDoc file : filesToDelete) {
864           if (monitor.isCanceled()) {
865             break;
866           }
867           monitor.setNote(file.getName());
868           boolean could = file.delete();
869           if (!could) undeletableFiles.add(file);
870           monitor.setProgress(progress++);
871         }
872 // if (! dir.exists()) dir.mkdirs (); // TODO: figure out where to put this.
873
return undeletableFiles;
874       }
875       public void complete(AsyncCompletionArgs<List JavaDoc<File JavaDoc>> args) {
876         // TODO: user feedback. Maybe add a method to the notifier to set the status bar text
877
}
878       public String JavaDoc getDiscriptionMessage() {
879         return "Deleting files...";
880       }
881     };
882     
883     public List JavaDoc<File JavaDoc> getClassFiles() {
884       File JavaDoc dir = this.getBuildDirectory ();
885       LinkedList JavaDoc<File JavaDoc> acc = new LinkedList JavaDoc<File JavaDoc>();
886       getClassFilesHelper(dir, acc);
887       if (! dir.exists()) dir.mkdirs();
888       return acc;
889     }
890     
891     private void getClassFilesHelper(File JavaDoc f, LinkedList JavaDoc<File JavaDoc> acc) {
892       if (f.isDirectory()) {
893         
894         File JavaDoc fs[] = f.listFiles(new FilenameFilter JavaDoc() {
895           public boolean accept(File JavaDoc parent, String JavaDoc name) {
896             return new File JavaDoc(parent, name).isDirectory() || name.endsWith(".class");
897           }
898         });
899         
900         if (fs!=null) { // listFiles may return null if there's an IO error
901
for (File JavaDoc kid: fs) { getClassFilesHelper(kid, acc); }
902         }
903         
904       } else if (f.getName().endsWith(".class")) acc.add(f);
905     }
906     
907     // ----- FIND ALL DEFINED CLASSES IN FOLDER ---
908

909     public ClassPathVector getExtraClassPath() { return _projExtraClassPath; }
910     public void setExtraClassPath(ClassPathVector cp) {
911       _projExtraClassPath = cp;
912       setClassPathChanged(true);
913     }
914   }
915  
916   protected FileGroupingState makeFlatFileGroupingState() { return new FlatFileGroupingState(); }
917  
918   class FlatFileGroupingState implements FileGroupingState {
919     public File JavaDoc getBuildDirectory() { return null; }
920     public File JavaDoc getProjectRoot() { return getWorkingDirectory(); }
921     public File JavaDoc getWorkingDirectory() {
922 // _log.log("AbstractGlobalModel.getWorkingDirectory() called");
923
try {
924         File JavaDoc[] roots = getSourceRootSet();
925         if (roots.length == 0) {
926           // return getMasterWorkingDirectory();
927
// use the last directory saved to the configuration
928
File JavaDoc file = null;
929 // _log.log("STICKY_INTERACTIONS_DIRECTORY flag is " + DrJava.getConfig().getSetting(STICKY_INTERACTIONS_DIRECTORY));
930
if (DrJava.getConfig().getSetting(STICKY_INTERACTIONS_DIRECTORY)) {
931             try {
932               // restore the path from the configuration
933
file = FileOps.getValidDirectory(DrJava.getConfig().getSetting(LAST_INTERACTIONS_DIRECTORY));
934 // _log.log("Last interactionsDirectory is " + file);
935
}
936             catch (RuntimeException JavaDoc e) { file = null; }
937           }
938           if (file == null) {
939             // something went wrong, clear the setting and use "user.home"
940
DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, FileOption.NULL_FILE);
941             file = FileOps.getValidDirectory(DrJava.getConfig().getSetting(LAST_INTERACTIONS_DIRECTORY));
942           }
943           // update the setting and return it
944
DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, file);
945 // _log.log("Returning " + file + " as working directory");
946
return file;
947         }
948 // _log.log("Returning " + roots[0].getCanonicalFile() + " as working directory");
949
return roots[0].getCanonicalFile();
950       }
951       catch(IOException JavaDoc e) { /* fall through */ }
952 // _log.log("Returning " + System.getProperty("user.home") + " as working directory");
953
return new File JavaDoc(System.getProperty("user.home")); // a flat file configuration should have exactly one source root
954
}
955     public boolean isProjectActive() { return false; }
956     public boolean inProjectPath(OpenDefinitionsDocument doc) { return false; }
957     public boolean inProjectPath(File JavaDoc f) { return false; }
958     public File JavaDoc getProjectFile() { return null; }
959     public void setBuildDirectory(File JavaDoc f) { }
960     public void setProjectFile(File JavaDoc f) { }
961     public void setProjectRoot(File JavaDoc f) { }
962     public void addAuxFile(File JavaDoc f) { }
963     public void remAuxFile(File JavaDoc f) { }
964     public void setWorkingDirectory(File JavaDoc f) { }
965     public File JavaDoc[] getProjectFiles() { return null; }
966     public boolean inProject(File JavaDoc f) { return false; }
967     public File JavaDoc getMainClass() { return null; }
968     public void setMainClass(File JavaDoc f) { }
969     public void setCreateJarFile(File JavaDoc f) { }
970     public File JavaDoc getCreateJarFile() { return null; }
971     public void setCreateJarFlags(int f) { }
972     public int getCreateJarFlags() { return 0; }
973     public ClassPathVector getExtraClassPath() { return new ClassPathVector(); }
974     public void setExtraClassPath(ClassPathVector cp) { }
975     public boolean isProjectChanged() { return false; }
976     public void setProjectChanged(boolean changed) { /* Do nothing */ }
977     public boolean isAuxiliaryFile(File JavaDoc f) { return false; }
978     
979     public void cleanBuildDirectory() { }
980     
981     public List JavaDoc<File JavaDoc> getClassFiles() { return new LinkedList JavaDoc<File JavaDoc>(); }
982   }
983  
984   /** Gives the title of the source bin for the navigator.
985    * @return The text used for the source bin in the tree navigator
986    */

987   public String JavaDoc getSourceBinTitle() { return "[ Source Files ]"; }
988  
989   /** Gives the title of the external files bin for the navigator
990    * @return The text used for the external files bin in the tree navigator.
991    */

992   public String JavaDoc getExternalBinTitle() { return "[ External Files ]"; }
993  
994   /** Gives the title of the aux files bin for the navigator.
995    * @return The text used for the aux files bin in the tree navigator.
996    */

997   public String JavaDoc getAuxiliaryBinTitle() { return "[ Included External Files ]"; }
998  
999   // ----- METHODS -----
1000

1001  /** Add a listener to this global model.
1002   * @param listener a listener that reacts on events generated by the GlobalModel.
1003   */

1004  public void addListener(GlobalModelListener listener) { _notifier.addListener(listener); }
1005 
1006  /** Remove a listener from this global model.
1007   * @param listener a listener that reacts on events generated by the GlobalModel
1008   * This method is synchronized using the readers/writers event protocol incorporated in EventNotifier<T>.
1009   */

1010  public void removeListener(GlobalModelListener listener) { _notifier.removeListener(listener); }
1011 
1012  // getter methods for the private fields
1013

1014  public DefinitionsEditorKit getEditorKit() { return _editorKit; }
1015 
1016  /** throws UnsupportedOperationException */
1017  public DefaultInteractionsModel getInteractionsModel() {
1018    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interaction");
1019  }
1020 
1021  /** throws UnsupportedOperationException */
1022  public InteractionsDJDocument getSwingInteractionsDocument() {
1023    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interaction");
1024  }
1025 
1026  /** throws UnsupportedOperationException */
1027  public InteractionsDocument getInteractionsDocument() {
1028    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interaction");
1029  }
1030 
1031  public ConsoleDocument getConsoleDocument() { return _consoleDoc; }
1032 
1033  public InteractionsDJDocument getSwingConsoleDocument() { return _consoleDocAdapter; }
1034 
1035  public PageFormat JavaDoc getPageFormat() { return _pageFormat; }
1036 
1037  public void setPageFormat(PageFormat JavaDoc format) { _pageFormat = format; }
1038  
1039  public CompilerModel getCompilerModel() {
1040    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support compilation");
1041  }
1042  
1043  /** throws UnsupportedOperationException */
1044  public int getNumCompErrors() {
1045    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support compilation");
1046  }
1047  
1048  /** throws UnsupportedOperationException */
1049  public void setNumCompErrors(int num) {
1050    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support compilation");
1051  };
1052 
1053  /** throws UnsupportedOperationException */
1054  public JUnitModel getJUnitModel() {
1055    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support unit testing");
1056  }
1057 
1058  /** throws UnsupportedOperationException */
1059  public JavadocModel getJavadocModel() {
1060    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support javadoc");
1061  }
1062 
1063  public IDocumentNavigator<OpenDefinitionsDocument> getDocumentNavigator() { return _documentNavigator; }
1064 
1065  public void setDocumentNavigator(IDocumentNavigator<OpenDefinitionsDocument> newnav) { _documentNavigator = newnav; }
1066 
1067  /** Creates a new open definitions document and adds it to the list. Public for testing purposes.
1068    * @return The new open document
1069    */

1070  public OpenDefinitionsDocument newFile(File JavaDoc parentDir) {
1071    final ConcreteOpenDefDoc doc = _createOpenDefinitionsDocument();
1072    doc.setParentDirectory(parentDir);
1073    doc.setFile(new NullFile());
1074    addDocToNavigator(doc);
1075    _notifier.newFileCreated(doc);
1076    return doc;
1077  }
1078
1079  /** Creates a new document, adds it to the list of open documents, and sets it to be active.
1080    * @return The new open document
1081    */

1082  public OpenDefinitionsDocument newFile() {
1083    File JavaDoc dir = _activeDirectory;
1084    if (dir == null) dir = getMasterWorkingDirectory();
1085// addToBrowserHistory();
1086
OpenDefinitionsDocument doc = newFile(dir);
1087    setActiveDocument(doc);
1088    return doc;
1089  }
1090                                               
1091  /** Creates a new junit test case.
1092   * @param name the name of the new test case
1093   * @param makeSetUp true iff an empty setUp() method should be included
1094   * @param makeTearDown true iff an empty tearDown() method should be included
1095   * @return the new open test case
1096   */

1097  public OpenDefinitionsDocument newTestCase(String JavaDoc name, boolean makeSetUp, boolean makeTearDown) {
1098    boolean elementary = (DrJava.getConfig().getSetting(LANGUAGE_LEVEL) == 1);
1099    
1100    final StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
1101    if (! elementary) buf.append("import junit.framework.TestCase;\n\n");
1102    buf.append("/**\n");
1103    buf.append("* A JUnit test case class.\n");
1104    buf.append("* Every method starting with the word \"test\" will be called when running\n");
1105    buf.append("* the test with JUnit.\n");
1106    buf.append("*/\n");
1107    if (! elementary) buf.append("public ");
1108    buf.append("class ");
1109    buf.append(name);
1110    buf.append(" extends TestCase {\n\n");
1111    if (makeSetUp) {
1112      buf.append("/**\n");
1113      buf.append("* This method is called before each test method, to perform any common\n");
1114      buf.append("* setup if necessary.\n");
1115      buf.append("*/\n");
1116      if (! elementary) buf.append("public ");
1117      buf.append("void setUp() throws Exception {\n}\n\n");
1118    }
1119    if (makeTearDown) {
1120      buf.append("/**\n");
1121      buf.append("* This method is called after each test method, to perform any common\n");
1122      buf.append("* clean-up if necessary.\n");
1123      buf.append("*/\n");
1124      if (! elementary) buf.append("public ");
1125      buf.append("void tearDown() throws Exception {\n}\n\n");
1126    }
1127    buf.append("/**\n");
1128    buf.append("* A test method.\n");
1129    buf.append("* (Replace \"X\" with a name describing the test. You may write as\n");
1130    buf.append ("* many \"testSomething\" methods in this class as you wish, and each\n");
1131    buf.append("* one will be called when running JUnit over this class.)\n");
1132    buf.append("*/\n");
1133    if (! elementary) buf.append("public ");
1134    buf.append("void testX() {\n}\n\n");
1135    buf.append("}\n");
1136    String JavaDoc test = buf.toString();
1137    
1138    OpenDefinitionsDocument openDoc = newFile();
1139    try {
1140      openDoc.insertString(0, test, null);
1141      openDoc.indentLines(0, test.length());
1142    }
1143    catch (BadLocationException JavaDoc ble) {
1144      throw new UnexpectedException(ble);
1145    }
1146    return openDoc;
1147  }
1148 
1149  /** This method is for use only by test cases. */
1150  public DocumentCache getDocumentCache() { return _cache; }
1151 
1152  //---------------------- Specified by ILoadDocuments ----------------------//
1153

1154  /** Open a file and add it to the pool of definitions documents. The provided file selector chooses a file,
1155   * and on a successful open, the fileOpened() event is fired. This method also checks if there was previously
1156   * a single unchanged, untitled document open, and if so, closes it after a successful opening.
1157   * @param com a command pattern command that selects what file to open
1158   * @return The open document, or null if unsuccessful
1159   * @exception IOException
1160   * @exception OperationCanceledException if the open was canceled
1161   * @exception AlreadyOpenException if the file is already open
1162   */

1163  public OpenDefinitionsDocument openFile(FileOpenSelector com) throws
1164    IOException JavaDoc, OperationCanceledException, AlreadyOpenException {
1165    // Close an untitled, unchanged document if it is the only one open
1166
boolean closeUntitled = _hasOneEmptyDocument();
1167    OpenDefinitionsDocument oldDoc = _activeDocument;
1168    OpenDefinitionsDocument openedDoc = openFileHelper(com);
1169    if (closeUntitled) closeFileHelper(oldDoc);
1170// Utilities.showDebug("DrJava has opened" + openedDoc + " and is setting it active");
1171
// addToBrowserHistory();
1172
setActiveDocument(openedDoc);
1173    setProjectChanged(true);
1174// Utilities.showDebug("active doc set; openFile returning");
1175
return openedDoc;
1176  }
1177 
1178  protected OpenDefinitionsDocument openFileHelper(FileOpenSelector com) throws IOException JavaDoc,
1179    OperationCanceledException, AlreadyOpenException {
1180    
1181    // This code is duplicated in MainFrame._setCurrentDirectory(File) for safety.
1182
final File JavaDoc file = (com.getFiles())[0].getCanonicalFile(); // may throw an IOException if path is invalid
1183
OpenDefinitionsDocument odd = _openFile(file);
1184// Utilities.showDebug("File " + file + " opened");
1185
// Make sure this is on the classpath
1186
try {
1187      File JavaDoc classPath = odd.getSourceRoot ();
1188      addDocToClassPath(odd);
1189      setClassPathChanged(true);
1190    }
1191    catch (InvalidPackageException e) {
1192      // Invalid package-- don't add it to classpath
1193
}
1194    
1195    return odd;
1196  }
1197 
1198  /** Open multiple files and add them to the pool of definitions documents. The provided file selector chooses
1199  * a collection of files, and on successfully opening each file, the fileOpened() event is fired. This method
1200  * also checks if there was previously a single unchanged, untitled document open, and if so, closes it after
1201  * a successful opening.
1202  * @param com a command pattern command that selects what file to open
1203  * @return The open document, or null if unsuccessful
1204  * @exception IOException
1205  * @exception OperationCanceledException if the open was canceled
1206  * @exception AlreadyOpenException if the file is already open
1207  */

1208  public OpenDefinitionsDocument[] openFiles(FileOpenSelector com)
1209    throws IOException JavaDoc, OperationCanceledException, AlreadyOpenException {
1210    
1211    // Close an untitled, unchanged document if it is the only one open
1212
boolean closeUntitled = _hasOneEmptyDocument();
1213    OpenDefinitionsDocument oldDoc = _activeDocument;
1214
1215    OpenDefinitionsDocument[] openedDocs = openFilesHelper(com);
1216    if (openedDocs.length > 0) {
1217      if (closeUntitled) closeFileHelper(oldDoc);
1218// addToBrowserHistory();
1219
setActiveDocument(openedDocs[0]);
1220    }
1221    return openedDocs;
1222  }
1223 
1224  protected OpenDefinitionsDocument[] openFilesHelper(FileOpenSelector com)
1225    throws IOException JavaDoc, OperationCanceledException, AlreadyOpenException {
1226    
1227    final File JavaDoc[] files = com.getFiles();
1228    if (files == null) { throw new IOException JavaDoc("No Files returned from FileSelector"); }
1229    OpenDefinitionsDocument[] docs = _openFiles(files);
1230    return docs;
1231  }
1232 
1233  // if set to true, and uncommented, the definitions document will
1234
// print out a small stack trace every time getDocument() is called
1235

1236  // static boolean SHOW_GETDOC = false;
1237

1238  /** Opens all the files in the list, and notifies about the last file opened. */
1239  private OpenDefinitionsDocument[] _openFiles(File JavaDoc[] files)
1240    throws IOException JavaDoc, OperationCanceledException, AlreadyOpenException {
1241    
1242    ArrayList JavaDoc<OpenDefinitionsDocument> alreadyOpenDocuments = new ArrayList JavaDoc<OpenDefinitionsDocument>();
1243    ArrayList JavaDoc<OpenDefinitionsDocument> retDocs = new ArrayList JavaDoc<OpenDefinitionsDocument>();
1244    
1245    // SHOW_GETDOC = true;
1246

1247    LinkedList JavaDoc<File JavaDoc> filesNotFound = new LinkedList JavaDoc<File JavaDoc>();
1248    LinkedList JavaDoc<OpenDefinitionsDocument> filesOpened = new LinkedList JavaDoc<OpenDefinitionsDocument>();
1249    for (final File JavaDoc f: files) {
1250      if (f == null) throw new IOException JavaDoc("File name returned from FileSelector is null");
1251      try {
1252        OpenDefinitionsDocument d = _rawOpenFile(IOUtil.attemptCanonicalFile(f));
1253        //always return last opened Doc
1254
retDocs.add(d);
1255        filesOpened.add(d);
1256      }
1257      catch (AlreadyOpenException aoe) {
1258        OpenDefinitionsDocument d = aoe.getOpenDocument();
1259        retDocs.add(d);
1260        alreadyOpenDocuments.add(d);
1261      }
1262      catch(FileNotFoundException JavaDoc e) { filesNotFound.add(f); }
1263    }
1264    
1265    for (final OpenDefinitionsDocument d: filesOpened) {
1266      _completeOpenFile(d); // contains view-related calls
1267
}
1268    // SHOW_GETDOC = false;
1269
for (File JavaDoc f: filesNotFound) { _notifier.fileNotFound(f); }
1270    
1271    if (! alreadyOpenDocuments.isEmpty()) {
1272      for(OpenDefinitionsDocument d : alreadyOpenDocuments) {
1273        _notifier.handleAlreadyOpenDocument(d);
1274        _notifier.fileOpened(d);
1275      }
1276    }
1277    
1278    if (retDocs != null) {
1279      return retDocs.toArray(new OpenDefinitionsDocument[0]);
1280    }
1281    else {
1282      //if we didn't open any files, then it's just as if they cancelled it...
1283
throw new OperationCanceledException();
1284    }
1285  }
1286 
1287 
1288  //----------------------- End ILoadDocuments Methods -----------------------//
1289

1290  /** Opens all files in the specified folder dir and places them in the appropriate places in the document navigator.
1291   * If "open folders recursively" is checked, this operation opens all files in the subtree rooted at dir.
1292   */

1293  public void openFolder(File JavaDoc dir, boolean rec) throws IOException JavaDoc, OperationCanceledException, AlreadyOpenException {
1294    if (dir == null) return; // just in case
1295

1296    if (dir.isDirectory()) {
1297      Iterable JavaDoc<File JavaDoc> filesIterable;
1298
1299      String JavaDoc extension = DrJavaRoot.LANGUAGE_LEVEL_EXTENSIONS[DrJava.getConfig().getSetting(LANGUAGE_LEVEL)];
1300      
1301      FileFilter JavaDoc match = IOUtil.predicateFileFilter(LambdaUtil.and(IOUtil.IS_FILE,
1302                                                                   IOUtil.extensionFilePredicate(extension)));
1303      if (rec) { filesIterable = IOUtil.listFilesRecursively(dir, match); }
1304      else { filesIterable = IOUtil.attemptListFilesAsIterable(dir, match); }
1305      List JavaDoc<File JavaDoc> files = IterUtil.asList(filesIterable);
1306
1307      if (isProjectActive()) {
1308        Collections.sort(files, new Comparator JavaDoc<File JavaDoc>() {
1309          public int compare(File JavaDoc o1,File JavaDoc o2) {
1310            return - o1.getAbsolutePath().compareTo(o2.getAbsolutePath());
1311          }
1312        });
1313      }
1314      else {
1315        Collections.sort(files, new Comparator JavaDoc<File JavaDoc>() {
1316          public int compare(File JavaDoc o1,File JavaDoc o2) {
1317            return - o1.getName().compareTo(o2.getName());
1318          }
1319        });
1320      }
1321      
1322      int ct = files.size();
1323      
1324      final File JavaDoc[] sfiles = files.toArray(new File JavaDoc[ct]);
1325      
1326      openFiles(new FileOpenSelector() { public File JavaDoc[] getFiles() { return sfiles; } });
1327      
1328      if (ct > 0 && _state.inProjectPath(dir)) setProjectChanged(true);
1329    }
1330  }
1331 
1332 
1333  /** Saves all open files, prompting for names if necessary.
1334   * When prompting (i.e., untitled document), set that document as active.
1335   * @param com a FileSaveSelector
1336   * @exception IOException
1337   */

1338   public void saveAllFiles(FileSaveSelector com) throws IOException JavaDoc {
1339     OpenDefinitionsDocument curdoc = getActiveDocument();
1340     saveAllFilesHelper(com);
1341     setActiveDocument(curdoc); // Return focus to previously active doc
1342
}
1343 
1344  /** Called by saveAllFiles in DefaultGlobalModel */
1345  protected void saveAllFilesHelper(FileSaveSelector com) throws IOException JavaDoc {
1346    
1347    boolean isProjActive = isProjectActive();
1348  
1349    for (final OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) { // getOpen... makes a copy
1350
// do not force Untitled document to be saved if projectActive() or unmodified
1351
if (doc.isUntitled() && (isProjActive || ! doc.isModifiedSinceSave())) continue;
1352      aboutToSaveFromSaveAll(doc);
1353      doc.saveFile(com);
1354    }
1355  }
1356 
1357  /** Creates a new FileGroupingState for specificed project file and default values for other properties.
1358   * @param projFile the new project file (which does not yet exist in the file system)
1359   */

1360  public void createNewProject(File JavaDoc projFile) { setFileGroupingState(new ProjectFileGroupingState(projFile)); }
1361    
1362  /** Configures a new project (created by createNewProject) and writes it to disk; only runs in event thread. */
1363  public void configNewProject() throws IOException JavaDoc {
1364    
1365    assert EventQueue.isDispatchThread();
1366    
1367// FileGroupingState oldState = _state;
1368
File JavaDoc projFile = getProjectFile();
1369    
1370    ProjectProfile builder = new ProjectProfile(projFile);
1371    
1372    // FileLists for project file
1373
ArrayList JavaDoc<DocFile> srcFileList = new ArrayList JavaDoc<DocFile>();
1374    LinkedList JavaDoc<DocFile> auxFileList = new LinkedList JavaDoc<DocFile>();
1375    ArrayList JavaDoc<File JavaDoc> extFileList = new ArrayList JavaDoc<File JavaDoc>();
1376    
1377    File JavaDoc projectRoot = builder.getProjectRoot();
1378    
1379// Utilities.show("Fetched project root is " + projectRoot);
1380

1381    ClassPathVector exCp = new ClassPathVector();
1382   
1383    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
1384      
1385      File JavaDoc f = doc.getFile();
1386      
1387      if (doc.isUntitled()) extFileList.add(f);
1388      else if (IOUtil.isMember(f, projectRoot)) {
1389        DocFile file = new DocFile(f);
1390        file.setPackage(doc.getPackageName()); // must save _packageName so it is correct when project is loaded
1391
builder.addSourceFile(file);
1392        srcFileList.add(file);
1393      }
1394      else if ( doc.isAuxiliaryFile()) {
1395        DocFile file = new DocFile(f);
1396        file.setPackage(doc.getPackageName()); // must save _packageName so it is correct when project is loaded
1397
builder.addAuxiliaryFile(new DocFile(f));
1398        auxFileList.add(file);
1399      }
1400      else /* doc is external file */ extFileList.add(f);
1401    }
1402    
1403// DocFile[] srcFiles = srcFileList.toArray(new DocFile[srcFileList.size()]);
1404
// DocFile[] extFiles = extFileList.toArray(new DocFile[extFileList.size()]);
1405

1406    // write to disk
1407
builder.write();
1408    
1409    _loadProject(builder);
1410  }
1411  /** Writes the project profile augmented by usage info to specified file. Assumes DrJava is in project mode.
1412   * @param file where to save the project
1413   * @param info
1414   */

1415  public ProjectProfile _makeProjectProfile(File JavaDoc file, Hashtable JavaDoc<OpenDefinitionsDocument, DocumentInfoGetter> info) throws IOException JavaDoc {
1416    ProjectProfile builder = new ProjectProfile(file);
1417    
1418    // add project root
1419
File JavaDoc pr = getProjectRoot();
1420    if (pr != null) builder.setProjectRoot(pr);
1421    
1422    // add opendefinitionsdocument
1423
ArrayList JavaDoc<File JavaDoc> srcFileList = new ArrayList JavaDoc<File JavaDoc>();
1424    LinkedList JavaDoc<File JavaDoc> auxFileList = new LinkedList JavaDoc<File JavaDoc>();
1425 
1426    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
1427      if (doc.inProjectPath()) {
1428        DocumentInfoGetter g = info.get(doc);
1429        builder.addSourceFile(g);
1430        srcFileList.add(g.getFile());
1431      }
1432      else if (doc.isAuxiliaryFile()) {
1433        DocumentInfoGetter g = info.get(doc);
1434        builder.addAuxiliaryFile(g);
1435        auxFileList.add(g.getFile());
1436      }
1437    }
1438      
1439    // add collapsed path info
1440
if (_documentNavigator instanceof JTreeSortNavigator) {
1441      String JavaDoc[] paths = ((JTreeSortNavigator<?>)_documentNavigator).getCollapsedPaths();
1442      for (String JavaDoc s : paths) { builder.addCollapsedPath(s); }
1443    }
1444    
1445    ClassPathVector exCp = getExtraClassPath();
1446    if (exCp != null) {
1447      ArrayList JavaDoc<File JavaDoc> exCpF = exCp.asFileVector();
1448      for (File JavaDoc f : exCpF) {
1449        builder.addClassPathFile(f);
1450        //System.out.println("Saving project classpath entry " + f);
1451
}
1452    }
1453// else System.err.println("Project ClasspathVector is null!");
1454

1455    // add build directory
1456
File JavaDoc bd = getBuildDirectory();
1457    if (bd != null) builder.setBuildDirectory(bd);
1458    
1459    // add working directory
1460
File JavaDoc wd = getWorkingDirectory(); // the value of the working directory to be stored in the project
1461
if (wd != null) builder.setWorkingDirectory(wd);
1462    
1463    // add jar main class
1464
File JavaDoc mainClass = getMainClass();
1465    if (mainClass != null) builder.setMainClass(mainClass);
1466    
1467    // add create jar file
1468
File JavaDoc createJarFile = getCreateJarFile();
1469    if (createJarFile != null) builder.setCreateJarFile(createJarFile);
1470    
1471    int createJarFlags = getCreateJarFlags();
1472    if (createJarFlags != 0) builder.setCreateJarFlags (createJarFlags);
1473    
1474    // add breakpoints and watches
1475
ArrayList JavaDoc<DebugBreakpointData> l = new ArrayList JavaDoc<DebugBreakpointData>();
1476    for(Breakpoint bp: getBreakpointManager().getRegions()) { l.add(bp); }
1477    builder.setBreakpoints(l);
1478    try {
1479      builder.setWatches(getDebugger().getWatches());
1480    }
1481    catch(DebugException de) { /* ignore, just don't store watches */ }
1482    
1483    // add bookmarks
1484
builder.setBookmarks(getBookmarkManager().getRegions());
1485    
1486    return builder;
1487  }
1488 
1489  /** Writes the project profile augmented by usage info to specified file. Assumes DrJava is in project mode.
1490   * @param file where to save the project
1491   */

1492  public void saveProject(File JavaDoc file, Hashtable JavaDoc<OpenDefinitionsDocument, DocumentInfoGetter> info) throws IOException JavaDoc {
1493    ProjectProfile builder = _makeProjectProfile(file, info);
1494    
1495    // write to disk
1496
builder.write();
1497    
1498// synchronized(_auxiliaryFiles) {
1499
// _auxiliaryFiles = new LinkedList<File>();
1500
// for (File f: builder.getAuxiliaryFiles()) { _auxiliaryFiles.add(f); }
1501
// }
1502

1503    ClassPathVector exCp = new ClassPathVector();
1504    for (File JavaDoc f : builder.getClassPaths()) { exCp.add(f); }
1505    setFileGroupingState(makeProjectFileGroupingState(builder.getProjectRoot(), builder.getMainClass (),
1506                                                      builder.getBuildDirectory(), builder.getWorkingDirectory(), file,
1507                                                      builder.getSourceFiles(), builder.getAuxiliaryFiles(), exCp,
1508                                                      builder.getCreateJarFile(), builder.getCreateJarFlags()));
1509  }
1510 
1511  public void reloadProject(File JavaDoc file, Hashtable JavaDoc<OpenDefinitionsDocument, DocumentInfoGetter> info) throws IOException JavaDoc {
1512    boolean projChanged = isProjectChanged();
1513    ProjectProfile builder = _makeProjectProfile(file, info);
1514    _loadProject(builder);
1515    setProjectChanged(projChanged);
1516  }
1517 
1518  /** Parses the given project file and loads it int the document navigator and resets interactions pane. Assumes
1519   * preceding project if any has already been closed.
1520   *
1521   * @param projectFile The project file to parse
1522   */

1523  public void openProject(File JavaDoc projectFile) throws IOException JavaDoc, MalformedProjectFileException {
1524    _loadProject(ProjectFileParser.ONLY.parse(projectFile));
1525  }
1526 
1527  /** Loads the specified project into the document navigator and opens all of the files (if not already open).
1528   * Assumes that any prior project has been closed. Only runs in event thread.
1529   * @param projectFile The project file to parse
1530   */

1531  private void _loadProject(final ProjectFileIR ir) throws IOException JavaDoc {
1532    
1533    assert EventQueue.isDispatchThread();
1534    
1535    final DocFile[] srcFiles = ir.getSourceFiles();
1536    final DocFile[] auxFiles = ir.getAuxiliaryFiles();
1537    final File JavaDoc projectFile = ir.getProjectFile();
1538    final File JavaDoc projectRoot = ir.getProjectRoot();
1539    final File JavaDoc buildDir = ir.getBuildDirectory ();
1540    final File JavaDoc workDir = ir.getWorkingDirectory();
1541    final File JavaDoc mainClass = ir.getMainClass();
1542    final File JavaDoc[] projectClassPaths = ir.getClassPaths();
1543    final File JavaDoc createJarFile = ir.getCreateJarFile ();
1544    int createJarFlags = ir.getCreateJarFlags();
1545    
1546    // clear browser history
1547
getBrowserHistoryManager().clearRegions();
1548    
1549    // set breakpoints
1550
getBreakpointManager().clearRegions();
1551    for (DebugBreakpointData dbd: ir.getBreakpoints()) {
1552      try {
1553        getDebugger().toggleBreakpoint(getDocumentForFile( dbd.getFile()), dbd.getOffset(), dbd.getLineNumber(),
1554                                       dbd.isEnabled());
1555      }
1556      catch(DebugException de) { /* ignore, just don't add breakpoint */ }
1557    }
1558    
1559    // set watches
1560
try { getDebugger().removeAllWatches(); }
1561    catch(DebugException de) { /* ignore, just don't remove old watches */ }
1562    for (DebugWatchData dwd: ir.getWatches()) {
1563      try { getDebugger().addWatch( dwd.getName()); }
1564      catch(DebugException de) { /* ignore, just don't add watch */ }
1565    }
1566    
1567    // set bookmarks
1568
getBookmarkManager().clearRegions();
1569    for (final DocumentRegion bm: ir.getBookmarks ()) {
1570      final OpenDefinitionsDocument odd = getDocumentForFile(bm.getFile());
1571      getBookmarkManager().addRegion(new SimpleDocumentRegion(odd, odd.getFile(), bm.getStartOffset(), bm.getEndOffset()));
1572    }
1573    
1574    final String JavaDoc projfilepath = projectRoot.getCanonicalPath();
1575    
1576    // Get the list of documents that are still open
1577
// final List<OpenDefinitionsDocument> oldDocs = getOpenDefintionsDocuments();
1578
// final FileGroupingState oldState = _state;
1579

1580// Utilities.showDebug("openProject called with file " + projectFile);
1581

1582    // Sets up the filters that cause documents to load in differentnsections of the tree. The names of these
1583
// sections are set from the methods such as getSourceBinTitle(). Changing this changes what is considered
1584
// source, aux, and external.
1585

1586    List JavaDoc<Pair<String JavaDoc, INavigatorItemFilter<OpenDefinitionsDocument>>> l =
1587        new LinkedList JavaDoc<Pair<String JavaDoc, INavigatorItemFilter<OpenDefinitionsDocument>>>();
1588    
1589    l.add(new Pair<String JavaDoc, INavigatorItemFilter<OpenDefinitionsDocument>>(getSourceBinTitle(),
1590        new INavigatorItemFilter<OpenDefinitionsDocument>() {
1591          public boolean accept(OpenDefinitionsDocument d) { return d.inProjectPath(); }
1592        }));
1593    
1594    l.add(new Pair<String JavaDoc, INavigatorItemFilter<OpenDefinitionsDocument>>(getAuxiliaryBinTitle(),
1595        new INavigatorItemFilter<OpenDefinitionsDocument>() {
1596          public boolean accept(OpenDefinitionsDocument d) { return d.isAuxiliaryFile(); }
1597        }));
1598    
1599    l.add(new Pair<String JavaDoc, INavigatorItemFilter<OpenDefinitionsDocument>>(getExternalBinTitle(),
1600        new INavigatorItemFilter<OpenDefinitionsDocument>() {
1601          public boolean accept(OpenDefinitionsDocument d) {
1602            return !(d.inProject() || d.isAuxiliaryFile()) || d.isUntitled();
1603          }
1604        }));
1605    
1606    IDocumentNavigator<OpenDefinitionsDocument> newNav =
1607      new AWTContainerNavigatorFactory<OpenDefinitionsDocument>().makeTreeNavigator(projfilepath, getDocumentNavigator(), l);
1608    
1609    setDocumentNavigator(newNav);
1610    
1611// synchronized(_auxiliaryFiles) {
1612
// _auxiliaryFiles.clear();
1613
// for (File file: auxFiles) { _auxiliaryFiles.add(file); }
1614
// }
1615

1616    ClassPathVector extraClassPaths = new ClassPathVector();
1617    for (File JavaDoc f : projectClassPaths) { extraClassPaths.add(f); }
1618    
1619// Utilities.show("Project Root loaded into grouping state is " + projRoot);
1620

1621    setFileGroupingState(makeProjectFileGroupingState(projectRoot, mainClass, buildDir, workDir, projectFile, srcFiles,
1622                                                      auxFiles, extraClassPaths, createJarFile, createJarFlags));
1623    
1624    resetInteractions(getWorkingDirectory()); // Shutdown debugger and reset interactions pane in new working directory
1625

1626    ArrayList JavaDoc<DocFile> projFiles = new ArrayList JavaDoc<DocFile>();
1627    DocFile active = null;
1628    for (DocFile f: srcFiles) {
1629      if (f.lastModified() > f.getSavedModDate()) f.setSavedModDate (f.lastModified());
1630      projFiles.add(f);
1631    }
1632    for (DocFile f: auxFiles) {
1633      if (f.lastModified() > f.getSavedModDate()) f.setSavedModDate (f.lastModified());
1634      projFiles.add(f);
1635    }
1636    // Insert active file as last file on list.
1637
if (active != null) projFiles.add(active);
1638    
1639// Utilities.showDebug("Project files are: " + projFiles);
1640

1641    final List JavaDoc<OpenDefinitionsDocument> projDocs = getProjectDocuments(); // project source files
1642

1643    // No files from the previous project (if any) can be open since it was already closed.
1644
// But all other files open at time this project is loaded are eligible for inclusion in the new project.
1645

1646    if (! projDocs.isEmpty())
1647// Utilities.invokeAndWait(new SRunnable() {
1648
// public void run() {
1649
for (OpenDefinitionsDocument d: projDocs) {
1650          try {
1651            final String JavaDoc path = fixPathForNavigator(d.getFile().getCanonicalPath());
1652            _documentNavigator.refreshDocument(d, path); // this operation must run in event thread
1653
}
1654          catch(IOException JavaDoc e) { /* Do nothing */ }
1655        }
1656// }
1657
// });
1658

1659// Utilities.showDebug("Preparing to refresh navigator GUI");
1660
// call on the GUI to finish up by opening the files and making necessary gui component changes
1661
final DocFile[] filesToOpen = projFiles.toArray(new DocFile[projFiles.size()]);
1662    _notifier.projectOpened(projectFile, new FileOpenSelector() {
1663      public File JavaDoc[] getFiles() { return filesToOpen; }
1664    });
1665    
1666    //Set active document from project file
1667
// if(active != null) { //TEMP
1668
// setActiveDocument(projDocs.get(projDocs.size() - 1));
1669
// }
1670
//OpenDefinitionsDocument.getCanonicalPath()
1671
//search for active document within opendefdocs
1672
// Utilities.show("Setting Active Document...");
1673
// if(active != null) {
1674
// String path = projFiles.get(projFiles.size() - 1).getCanonicalPath();
1675
// Utilities.show("Active document path: " + path);
1676
// for(OpenDefinitionsDocument doc: projDocs) {
1677
// Utilities.show("Searching path: " + doc.getCanonicalPath());
1678
// if(doc.getCanonicalPath().compareTo(path) == 0) {
1679
// setActiveDocument(doc);
1680
// Utilities.show("New active document set");
1681
// break;
1682
// }
1683
// }
1684
// }
1685

1686    if (_documentNavigator instanceof JTreeSortNavigator)
1687      ((JTreeSortNavigator<?>)_documentNavigator).collapsePaths(ir.getCollapsedPaths());
1688  }
1689 
1690  /** Performs any needed operations on the model after project files have been closed. This method is not
1691    * responsible for closing any filesl both the files in the project and the project file have already been
1692    * closed (by MainFrame._closeProject()). Resets interations unless suppressReset is true.
1693    */

1694  public void closeProject(boolean suppressReset) {
1695    setDocumentNavigator(new AWTContainerNavigatorFactory<OpenDefinitionsDocument>().
1696                           makeListNavigator(getDocumentNavigator()));
1697    setFileGroupingState(makeFlatFileGroupingState());
1698   
1699    if (! suppressReset) resetInteractions(getWorkingDirectory());
1700    _notifier.projectClosed();
1701  }
1702 
1703  /** If the document is untitled, brings it to the top so that the
1704   * user will know which is being saved.
1705   */

1706  public void aboutToSaveFromSaveAll(OpenDefinitionsDocument doc) {
1707    if ( doc.isUntitled()) setActiveDocument(doc);
1708  }
1709 
1710  /** Closes an open definitions document, prompting to save if the document has been changed. Returns whether
1711   * the file was successfully closed. Also ensures the invariant that there is always at least
1712   * one open document holds by creating a new file if necessary.
1713   * @return true if the document was closed
1714   */

1715   public boolean closeFile(OpenDefinitionsDocument doc) {
1716     List JavaDoc<OpenDefinitionsDocument> list = new LinkedList JavaDoc<OpenDefinitionsDocument>();
1717     list.add(doc);
1718     return closeFiles(list);
1719   }
1720 
1721  /** Attempts to close all open documents. Also ensures the invariant that there is always at least
1722    * one open document holds by creating a new file if necessary. Resets interactions iff operation succeeds.
1723    * @return true if all documents were closed
1724    */

1725   public boolean closeAllFiles() {
1726     List JavaDoc<OpenDefinitionsDocument> docs = getOpenDefinitionsDocuments();
1727     boolean res = closeFiles(docs);
1728     if (res) {
1729// _log.log("Resetting interactions pane to use " + getWorkingDirectory() + " as working directory");
1730
resetInteractions(getWorkingDirectory());
1731     }
1732     return res;
1733   }
1734 
1735  /** This function closes a group of files assuming that the files are contiguous in the enumeration
1736   * provided by the document navigator. This assumption is used in selecting which remaining document
1737   * (if any) to activate.
1738   * <p>
1739   * The corner cases in which the file that is being closed had been externally
1740   * deleted have been addressed in a few places, namely DefaultGlobalModel.canAbandonFile()
1741   * and MainFrame.ModelListener.canAbandonFile(). If the DefinitionsDocument for the
1742   * OpenDefinitionsDocument being closed is not in the cache (see model.cache.DocumentCache)
1743   * then it is closed without prompting the user to save it. If it is in the cache, then
1744   * we can successfully notify the user that the file is selected for closing and ask whether to
1745   * saveAs, close, or cancel.
1746   * @param docs the list od OpenDefinitionsDocuments to close
1747   * @return whether all files were closed
1748   */

1749  public boolean closeFiles(List JavaDoc<OpenDefinitionsDocument> docs) {
1750    if (docs.size() == 0) return true;
1751    
1752    _log.log("closeFiles(" + docs + ") called");
1753    /* Force the user to save or discard all modified files in docs */
1754    for (OpenDefinitionsDocument doc : docs) {
1755      if (! doc.canAbandonFile()) return false; }
1756    
1757    /* If all files are being closed, create a new file before starting in order to have a potentially active file
1758     * that is not in the list of closing files. */

1759    if (docs.size() == getOpenDefinitionsDocumentsSize()) newFile();
1760    
1761    /* Set the active document to the document just after the last document or the document just before the first
1762     * document in docs. The new file created above (if necessary) does not appear in docs. */

1763    _ensureNotActive(docs);
1764        
1765    // Close the files in docs.
1766
for (OpenDefinitionsDocument doc : docs) { closeFileWithoutPrompt(doc); }
1767    return true;
1768  }
1769 
1770  /** Helper for closeFile. This method was the closeFile(...) method before projects were added to DrJava. */
1771  protected boolean closeFileHelper(OpenDefinitionsDocument doc) {
1772    // System.err.println("closing " + doc);
1773
boolean canClose = doc.canAbandonFile();
1774    if (canClose) return closeFileWithoutPrompt(doc);
1775    return false;
1776  }
1777 
1778  /** Closes an open definitions document, without prompting to save if the document has been changed. Returns
1779   * whether the file was successfully closed. NOTE: This method should not be called unless it can be
1780   * absolutely known that the document being closed is not the active document. The closeFile() method in
1781   * SingleDisplayModel ensures that a new active document is set, but closeFileWithoutPrompt is not.
1782   * @return true if the document was closed.
1783   */

1784  public boolean closeFileWithoutPrompt(final OpenDefinitionsDocument doc) {
1785    // new Exception("Closed document " + doc).printStackTrace();
1786

1787    _log.log("closeFileWithoutPrompt(" + doc + ") called; getRawFile() = " + doc.getRawFile());
1788    _log.log("_documentsRepos = " + _documentsRepos);
1789    boolean found;
1790    synchronized(_documentsRepos) { found = (_documentsRepos.remove(doc.getRawFile()) != null); }
1791    
1792    if (! found) {
1793      _log.log("Cannot close " + doc + "; not found!");
1794      return false;
1795    }
1796        
1797    // remove regions for this file
1798
doc.getBreakpointManager().clearRegions();
1799    doc.getBookmarkManager().clearRegions();
1800    for (RegionManager<MovingDocumentRegion> rm: doc.getFindResultsManagers()) rm.clearRegions();
1801    doc.getBrowserHistoryManager().clearRegions();
1802    
1803    Utilities.invokeLater (new SRunnable() {
1804      public void run() { _documentNavigator.removeDocument(doc); } // this operation must run in event thread
1805
});
1806    _notifier.fileClosed(doc);
1807    doc.close();
1808    return true;
1809  }
1810 
1811  /** Closes all open documents. This operation can be cancelled by the user since it
1812   * checks if all files can be abandoned BEFORE it actually modifies the project state.
1813   * @return {@code false} if the user cancelled
1814   */

1815  public boolean closeAllFilesOnQuit() {
1816    
1817    List JavaDoc<OpenDefinitionsDocument> docs = getOpenDefinitionsDocuments();
1818    
1819    // first see if the user wants to cancel on any of them
1820
OpenDefinitionsDocument retainedDoc = null;
1821  
1822    for (OpenDefinitionsDocument doc : docs) {
1823      if (! doc.canAbandonFile()) { retainedDoc = doc; break; }
1824    }
1825    
1826    if (retainedDoc != null) { // the user did want to cancel
1827
// _log.log("closeAllFilesOnQuit failed. Retained doc = " + retainedDoc);
1828
return false;
1829    }
1830    
1831    // user did not want to cancel, close all of them
1832
// All files are being closed, create a new file before starting in order to have
1833
// a potentially active file that is not in the list of closing files.
1834
newFile();
1835    
1836    // Set the active document to the document just after the last document or the document just before the
1837
// first document in docs. A new file does not appear in docs.
1838
_ensureNotActive(docs);
1839        
1840    // Close the files in docs.
1841
for (OpenDefinitionsDocument doc : docs) { closeFileWithoutPrompt(doc); }
1842    
1843    return true;
1844  }
1845 
1846   /** Exits the program. Only quits if all documents are successfully closed. */
1847  public void quit() { quit(false); }
1848  
1849   /** Halts the program immediately. */
1850  public void forceQuit() { quit(true); }
1851  
1852  /** Exits the program. If force is true, quits regardless of whether all documents are successfully closed. */
1853  private void quit(boolean force) {
1854// _log.log("quit(" + force + ") called");
1855
try {
1856      if (! force && ! closeAllFilesOnQuit()) return;
1857      
1858      /* [ 1478796 ] DrJava Does Not Shut Down With Project Open. On HP tc1100 and Toshiba Portege tablet PCs, there
1859       * appears to be a problem in a shutdown hook, presumably the RMI shutdown hook. Shutdown hooks get executed in
1860       * Runtime.exit (to which System.exit delegates), and if a shutdown hook does not complete, the VM does not shut
1861       * down. The difference between Runtime.halt and Runtime.exit is that Runtime.exit runs the shutdown hooks and
1862       * the finalizers (if Runtime.runFinalizersOnExit(true) has been called); then it calls Runtime.halt. The RMI
1863       * hooks are potentially important in running unit test that repeatedly start and stop DrJava, so we only invoke
1864       * Runtime.halt if our attempt to exit times out.
1865       */

1866      
1867      shutdown(force);
1868    }
1869    catch(Throwable JavaDoc t) { shutdown(true); /* force exit anyway */ }
1870  }
1871  
1872  /* Terminates DrJava via System.exit with Runtime.halt as a backup if the former gets hung up. */
1873  private void shutdown(boolean force) {
1874    if (force) Runtime.getRuntime().halt(0);
1875    
1876    dispose(); // kills interpreter and cleans up RMI hooks in the slave JVM
1877

1878    if (DrJava.getConfig().getSetting(OptionConstants.DRJAVA_USE_FORCE_QUIT)) {
1879      Runtime.getRuntime().halt(0); // force DrJava to exit
1880
}
1881    
1882    Thread JavaDoc monitor = new Thread JavaDoc(new Runnable JavaDoc() {
1883      public void run() {
1884        try { Thread.sleep(2000); }
1885        catch(InterruptedException JavaDoc e) { /* proceed */ }
1886        Runtime.getRuntime().halt(0); // force DrJava to exit if it still alive
1887
}
1888    });
1889    monitor.setDaemon(true);
1890    monitor.start();
1891    System.exit(0);
1892  }
1893    
1894  /** Prepares this model to be thrown away. Never called outside of tests. This version ignores the slave JVM. */
1895  public void dispose() {
1896    synchronized(_documentsRepos) { _documentsRepos.clear(); }
1897    Utilities.invokeLater(new SRunnable() {
1898      public void run() { _documentNavigator.clear(); } // this operation must run in event thread
1899
});
1900    // Only remove listeners after pending events have completed
1901
SwingUtilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.removeAllListeners(); } });
1902  }
1903 
1904  /** Disposes of external resources. This is a no op in AbstractGlobalModel. */
1905  public void disposeExternalResources() { /* no op */ }
1906
1907  /** Gets the document for the specified file; may involve opening the file. */
1908  public OpenDefinitionsDocument getDocumentForFile(File JavaDoc file) throws IOException JavaDoc {
1909    // Check if this file is already open
1910
OpenDefinitionsDocument doc = _getOpenDocument(file);
1911    if (doc == null) {
1912      // If not, open and return it
1913
final File JavaDoc f = file;
1914      FileOpenSelector selector =
1915        new FileOpenSelector() { public File JavaDoc[] getFiles() { return new File JavaDoc[] {f}; } };
1916      try { doc = openFile(selector);}
1917      catch (AlreadyOpenException e) { doc = e.getOpenDocument(); }
1918      catch (OperationCanceledException e) { throw new UnexpectedException(e); /* Cannot happen */ }
1919    }
1920    return doc;
1921  }
1922
1923  /** Iterates over OpenDefinitionsDocuments, looking for this file.
1924   * TODO: This is not very efficient!
1925   */

1926  public boolean isAlreadyOpen(File JavaDoc file) { return (_getOpenDocument(file) != null); }
1927
1928  /** Returns the OpenDefinitionsDocument corresponding to the INavigatorItem/DefinitionsDocument passed in.
1929   * @param doc the searched for Document
1930   * @return its corresponding OpenDefinitionsDocument
1931   */

1932  public OpenDefinitionsDocument getODDForDocument(AbstractDocumentInterface doc) {
1933    /** This function needs to be phased out altogether; the goal is for the OpenDefinitionsDocument
1934     * to also function as its own Document, so this function will be useless
1935     */

1936    if (doc instanceof OpenDefinitionsDocument) return (OpenDefinitionsDocument) doc;
1937    if (doc instanceof DefinitionsDocument) return ((DefinitionsDocument) doc).getOpenDefDoc();
1938    throw new IllegalStateException JavaDoc("Could not get the OpenDefinitionsDocument for Document: " + doc);
1939  }
1940
1941  /** Gets a DocumentIterator to allow navigating through open Swing Documents. */
1942  public DocumentIterator getDocumentIterator() { return this; }
1943
1944  /** Returns the ODD preceding the given document in the document list.
1945   * @param d the current Document
1946   * @return the next Document
1947   */

1948  public OpenDefinitionsDocument getNextDocument(OpenDefinitionsDocument d) {
1949    OpenDefinitionsDocument nextdoc = null; // irrelevant initialization required by javac
1950
// try {
1951
OpenDefinitionsDocument doc = getODDForDocument(d);
1952      nextdoc = _documentNavigator.getNext(doc);
1953      if (nextdoc == doc) nextdoc = _documentNavigator.getFirst(); // wrap around if necessary
1954
OpenDefinitionsDocument res = getNextDocHelper(nextdoc);
1955// Utilities.showDebug("nextDocument(" + d + ") = " + res);
1956
return res;
1957// }
1958
// catch(DocumentClosedException dce) { return getNextDocument(nextdoc); }
1959
}
1960 
1961  private OpenDefinitionsDocument getNextDocHelper(OpenDefinitionsDocument nextdoc) {
1962    if ( nextdoc.isUntitled() || nextdoc.verifyExists()) return nextdoc;
1963    // Note: verifyExists prompts user for location of the file if it is not found
1964

1965    // cannot find nextdoc; move on to next document
1966
return getNextDocument(nextdoc);
1967  }
1968
1969  /** Returns the ODD preceding the given document in the document list.
1970   * @param d the current Document
1971   * @return the previous Document
1972   */

1973  public OpenDefinitionsDocument getPrevDocument(OpenDefinitionsDocument d) {
1974    OpenDefinitionsDocument prevdoc = null; // irrelevant initialization required by javac
1975
// try {
1976
OpenDefinitionsDocument doc = getODDForDocument(d);
1977      prevdoc = _documentNavigator.getPrevious(doc);
1978      if (prevdoc == doc) prevdoc = _documentNavigator.getLast(); // wrap around if necessary
1979
return getPrevDocHelper(prevdoc);
1980// }
1981
// catch(DocumentClosedException dce) { return getPrevDocument(prevdoc); }
1982
}
1983 
1984  private OpenDefinitionsDocument getPrevDocHelper(OpenDefinitionsDocument prevdoc) {
1985    if (prevdoc.isUntitled() || prevdoc.verifyExists()) return prevdoc;
1986    // Note: verifyExists() prompts user for location of prevdoc
1987

1988    // cannot find prevdoc; move on to preceding document
1989
return getPrevDocument(prevdoc);
1990  }
1991
1992  public int getDocumentCount() { return _documentsRepos.size(); }
1993 
1994  /** Returns a new collection of all documents currently open for editing. This is equivalent to the results of
1995    * getDocumentForFile for the set of all files for which isAlreadyOpen returns true.
1996    * @return a random-access List of the open definitions documents..
1997    */

1998  public List JavaDoc<OpenDefinitionsDocument> getOpenDefinitionsDocuments() {
1999    synchronized(_documentsRepos) {
2000      ArrayList JavaDoc<OpenDefinitionsDocument> docs = new ArrayList JavaDoc<OpenDefinitionsDocument>(_documentsRepos.size());
2001      for (OpenDefinitionsDocument doc: _documentsRepos.values()) { docs.add(doc); }
2002      return docs;
2003    }
2004  }
2005  /* Returns a sorted (by time of insertion) collection of all open documents. */
2006  public List JavaDoc<OpenDefinitionsDocument> getSortedOpenDefinitionsDocuments() { return getOpenDefinitionsDocuments(); }
2007 
2008  /** @return the size of the collection of OpenDefinitionsDocuments */
2009  public int getOpenDefinitionsDocumentsSize() { synchronized(_documentsRepos) { return _documentsRepos.size(); } }
2010 
2011  /** @return true if all open documents are in sync with their primary class files. */
2012  public boolean hasOutOfSyncDocuments() {
2013    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
2014      if (doc.isSourceFile() && ! doc.checkIfClassFileInSync()) {
2015// Utilities.show("Out of sync document is: " + doc);
2016
return true;
2017      }
2018    }
2019    return false;
2020  }
2021 
2022  /**
2023   * Set the indent tab size for all definitions documents.
2024   * @param indent the number of spaces to make per level of indent
2025   */

2026  void setDefinitionsIndent(int indent) {
2027    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) { doc.setIndent(indent); }
2028  }
2029
2030  /** A degenerate operation since this has no slave JVM and no interactions model. */
2031  public void resetInteractions(File JavaDoc wd) { /* do nothing */ }
2032 
2033  /** A degenerate operation since this has no slave JVM and no interactions model. */
2034  public void resetInteractions(File JavaDoc wd, boolean forceReset) { /* do nothing */ }
2035
2036  /** Resets the console. Fires consoleReset() event. */
2037  public void resetConsole() {
2038    _consoleDoc.reset("");
2039    _notifier.consoleReset();
2040  }
2041
2042  /** throw new UnsupportedOperationException */
2043  public void interpretCurrentInteraction() {
2044    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2045  }
2046
2047  /** throws UnsupportedOperationException */
2048  public void loadHistory(FileOpenSelector selector) throws IOException JavaDoc {
2049    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2050  }
2051
2052  /** throws UnsupportedOperationException */
2053  public InteractionsScriptModel loadHistoryAsScript(FileOpenSelector selector) throws
2054    IOException JavaDoc, OperationCanceledException {
2055    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2056  }
2057
2058  /** throws UnsupportedOperationException */
2059  public void clearHistory() {
2060    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2061  }
2062
2063  /** throws UnsupportedOperationException */
2064  public void saveHistory(FileSaveSelector selector) throws IOException JavaDoc {
2065    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2066  }
2067
2068  /** throws UnsupportedOperationException */
2069  public void saveHistory(FileSaveSelector selector, String JavaDoc editedVersion) throws IOException JavaDoc {
2070    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2071  }
2072
2073  /** Returns the entire history as a String with semicolons as needed. */
2074  public String JavaDoc getHistoryAsStringWithSemicolons() {
2075    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2076  }
2077
2078  /** Throws UnsupportedOperationException */
2079  public String JavaDoc getHistoryAsString() {
2080    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2081  }
2082
2083  /** Registers OptionListeners. Factored out code from the two constructor. */
2084  private void _registerOptionListeners() {
2085// // Listen to any relevant config options
2086
// DrJava.getConfig().addOptionListener(EXTRA_CLASSPATH, new ExtraClasspathOptionListener());
2087

2088    DrJava.getConfig().addOptionListener(BACKUP_FILES, new BackUpFileOptionListener());
2089    Boolean JavaDoc makeBackups = DrJava.getConfig().getSetting(BACKUP_FILES);
2090    FileOps.DefaultFileSaver.setBackupsEnabled(makeBackups.booleanValue ());
2091
2092// DrJava.getConfig().addOptionListener(ALLOW_PRIVATE_ACCESS, new OptionListener<Boolean>() {
2093
// public void optionChanged(OptionEvent<Boolean> oce) {
2094
// getInteractionsModel().setPrivateAccessible( oce.value.booleanValue());
2095
// }
2096
// });
2097
}
2098
2099  /** Appends a string to the given document using a particular attribute set.
2100   * Also waits for a small amount of time (WRITE_DELAY) to prevent any one
2101   * writer from flooding the model with print calls to the point that the
2102   * user interface could become unresponsive.
2103   * @param doc Document to append to
2104   * @param s String to append to the end of the document
2105   * @param style the style to print with
2106   */

2107  protected void _docAppend(ConsoleDocument doc, String JavaDoc s, String JavaDoc style) {
2108    synchronized(_systemWriterLock) {
2109      try {
2110        doc.insertBeforeLastPrompt (s, style);
2111        
2112        // Wait to prevent being flooded with println's
2113
_systemWriterLock.wait(WRITE_DELAY);
2114      }
2115      catch (InterruptedException JavaDoc e) {
2116        // It's ok, we'll go ahead and resume
2117
}
2118    }
2119  }
2120
2121
2122  /** Prints System.out to the DrJava console. */
2123  public void systemOutPrint(String JavaDoc s) {_docAppend(_consoleDoc, s, ConsoleDocument.SYSTEM_OUT_STYLE); }
2124
2125  /** Prints System.err to the DrJava console. */
2126  public void systemErrPrint(String JavaDoc s) { _docAppend(_consoleDoc, s, ConsoleDocument.SYSTEM_ERR_STYLE); }
2127
2128  /** Prints the given string to the DrJava console as an echo of System.in */
2129  public void systemInEcho(String JavaDoc s) { _docAppend(_consoleDoc, s, ConsoleDocument.SYSTEM_IN_STYLE); }
2130 
2131  /** throws UnsupportedOperationException */
2132  public void printDebugMessage(String JavaDoc s) {
2133    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support debugging");
2134  }
2135
2136
2137  /** throw new UnsupportedOperationException */
2138  public void waitForInterpreter() {
2139    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2140  }
2141
2142
2143  /** throws new UnsupportedOperationException */
2144  public ClassPathVector getInteractionsClassPath() {
2145    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support interactions");
2146  }
2147 
2148  /** Returns a project's extra classpaths; empty for FlatFileGroupingState
2149   * @return The classpath entries loaded along with the project
2150   */

2151  public ClassPathVector getExtraClassPath() { return _state.getExtraClassPath(); }
2152 
2153  /** Sets the set of classpath entries to use as the projects set of classpath entries. This is normally used by the
2154   * project preferences..
2155   */

2156  public void setExtraClassPath(ClassPathVector cp) {
2157    _state.setExtraClassPath(cp);
2158    setClassPathChanged(true);
2159    //System.out.println("Setting project classpath to: " + cp);
2160
}
2161
2162  /** Gets an array of all sourceRoots for the open definitions documents, without duplicates. Note that if any of
2163   * the open documents has an invalid package statement, it won't be adde to the source root set. On 8.7.02
2164   * changed the sourceRootSet such that the directory DrJava was executed from is now after the sourceRoots
2165   * of the currently open documents in order that whatever version the user is looking at corresponds to the
2166   * class file the interactions window uses.
2167   * TODO: Fix out of date comment, possibly remove this here?
2168   */

2169  public File JavaDoc[] getSourceRootSet() {
2170    HashSet JavaDoc<File JavaDoc> roots = new HashSet JavaDoc<File JavaDoc>();
2171
2172    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
2173      try {
2174        if (! doc.isUntitled()) {
2175          File JavaDoc root = doc.getSourceRoot ();
2176          if (root != null) roots.add(root); // Can't create duplicate entries in a HashSet
2177
}
2178      }
2179      catch (InvalidPackageException e) {
2180// Utilities.show("InvalidPackageException in getSourceRootSet");
2181
/* file has invalid package statement; ignore it */
2182      }
2183    }
2184    return roots.toArray(new File JavaDoc[roots.size()]);
2185  }
2186
2187// /** Return the absolute path of the file with the given index, or "(untitled)" if no file exists. */
2188
// public String getDisplayFullPath(int index) {
2189
// OpenDefinitionsDocument doc = getOpenDefinitionsDocuments().get(index);
2190
// if (doc == null) throw new RuntimeException( "Document not found with index " + index);
2191
// return doc.getDisplayFullPath();
2192
// }
2193

2194  /** throws UnsupportedOperationException */
2195  public Debugger getDebugger() {
2196    // throw new UnsupportedOperationException("AbstractGlobalModel does not support debugging");
2197
return NoDebuggerAvailable.ONLY;
2198  }
2199
2200  /** throws UnsupportedOperationException */
2201  public int getDebugPort() throws IOException JavaDoc {
2202    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support debugging");
2203  }
2204
2205  /** Checks if any open definitions documents have been modified since last being saved.
2206   * @return whether any documents have been modified
2207   */

2208  public boolean hasModifiedDocuments() {
2209    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
2210      if (doc.isModifiedSinceSave()) return true;
2211    }
2212    return false;
2213  }
2214 
2215  /** Checks if any open definitions documents are untitled.
2216   * @return whether any documents are untitled
2217   */

2218  public boolean hasUntitledDocuments() {
2219    for (OpenDefinitionsDocument doc: getOpenDefinitionsDocuments()) {
2220      if (doc.isUntitled()) return true;
2221    }
2222    return false;
2223  }
2224
2225  /** Searches for a file with the given name on the current source roots and the augmented classpath.
2226   * @param fileName name of the source file to look for
2227   * @return the file corresponding to the given name, or null if it cannot be found
2228   */

2229  public File JavaDoc getSourceFile(String JavaDoc fileName) {
2230    File JavaDoc[] sourceRoots = getSourceRootSet();
2231    for (File JavaDoc s: sourceRoots) {
2232      File JavaDoc f = _getSourceFileFromPath(fileName, s);
2233      if (f != null) return f;
2234    }
2235    Vector JavaDoc<File JavaDoc> sourcepath = DrJava.getConfig().getSetting(OptionConstants.DEBUG_SOURCEPATH);
2236    return findFileInPaths(fileName, sourcepath);
2237  }
2238
2239  /** Searches for a file with the given name on the provided paths. Returns null if the file is not found.
2240   * @param fileName Name of the source file to look for
2241   * @param paths An array of directories to search
2242   * @return the file if it is found, or null otherwise
2243   */

2244  public File JavaDoc findFileInPaths(String JavaDoc fileName, List JavaDoc<File JavaDoc> paths) {
2245    for (File JavaDoc p: paths) {
2246      File JavaDoc f = _getSourceFileFromPath(fileName, p);
2247      if (f != null) return f;
2248    }
2249    return null;
2250  }
2251
2252  /** Gets the file named filename from the given path, if it exists. Returns null if it's not there.
2253   * @param filename the file to look for
2254   * @param path the path to look for it in
2255   * @return the file if it exists
2256   */

2257  private File JavaDoc _getSourceFileFromPath(String JavaDoc fileName, File JavaDoc path) {
2258    String JavaDoc root = path.getAbsolutePath();
2259    File JavaDoc f = new File JavaDoc(root + System.getProperty("file.separator") + fileName);
2260    return f.exists() ? f : null;
2261  }
2262  
2263  private static volatile int ID_COUNTER = 0; /* Seed for assigning id numbers to OpenDefinitionsDocuments */
2264 
2265  // ---------- ConcreteRegionManager inner class -------
2266
/** Simple region manager for the entire model. Follows readers/writers locking protocol of EventNotifier. */
2267  static class ConcreteRegionManager<R extends DocumentRegion> extends EventNotifier<RegionManagerListener<R>>
2268    implements RegionManager<R> {
2269    /** Vector of regions. Primitive operations are thread safe. */
2270    protected volatile Vector JavaDoc<R> _regions = new Vector JavaDoc<R>();
2271    protected volatile R _current = null;
2272    protected volatile int _maxSize;
2273    
2274    /** Create a new ConcreteRegionManager with the specified maximum size.
2275      * @param size maximum number of regions that can be stored in this manager.
2276      */

2277    public ConcreteRegionManager(int size) { _maxSize = size; }
2278    
2279    /** Create a new ConcreteRegionManager without maximum size. */
2280    public ConcreteRegionManager() { this(0); }
2281    
2282    /** Returns the region in this manager at the given offset, or null if one does not exist.
2283      * @param odd the document
2284      * @param offset the offset in the document
2285      * @return the DocumentRegion at the given line number, or null if it does not exist.
2286      */

2287    public R getRegionAt(OpenDefinitionsDocument odd, int offset) {
2288      for (R r: _regions) {
2289        if (r.getDocument().equals(odd) && offset >= r.getStartOffset() && offset <= r.getEndOffset()) return r;
2290      }
2291      return null;
2292    }
2293    
2294    /** Get the DocumentRegion that is stored in this RegionsTreePanel overlapping the area for the given document,
2295      * or null if it doesn't exist.
2296      * @param odd the document
2297      * @param startOffset the start offset
2298      * @param endOffset the end offset
2299      * @return the DocumentRegion or null
2300      */

2301    public R getRegionOverlapping(OpenDefinitionsDocument odd, int startOffset, int endOffset) {
2302      for (R r: _regions) {
2303        if (! (r.getDocument().equals(odd))) { continue; }
2304        
2305        if ((r.getStartOffset() >= startOffset && r.getEndOffset() <= endOffset) || // r contained in startOffset-endOffset
2306
(r.getStartOffset() <= startOffset && r.getEndOffset() >= endOffset) || // startOffset-endOffset contained in r
2307
(r.getStartOffset() >= startOffset && r.getStartOffset() <= endOffset) || // r starts within startOffset-endOffset
2308
(r.getEndOffset() >= startOffset && r.getEndOffset() <= endOffset)) { // r ends within startOffset-endOffset
2309
// already there
2310
return r;
2311        }
2312      }
2313      // not found
2314
return null;
2315    }
2316    
2317    /** @return the index of the region in the vector, or -1 if not found. Uses ==. */
2318    protected int getIndexOf(R region) {
2319      int index = 0;
2320      for (R r: _regions) {
2321        if (region == r) return index;
2322        else ++index;
2323      }
2324      return -1;
2325    }
2326    
2327    /** Add the supplied DocumentRegion to the manager. Only runs in event thread after initialization?
2328      * @param region the DocumentRegion to be inserted into the manager
2329      * @param index the index at which the DocumentRegion was inserted
2330      */

2331    public void addRegion(final R region) {
2332      int index = getIndexOf(_current);
2333      // only add if current, previous, and next are not already the region; prevents trivial duplicates
2334
if (! region.equals(_current) &&
2335          (index == _regions.size() - 1 || ! region.equals(_regions.get(index + 1))) &&
2336          (index <= 0 || ! region.equals(_regions.get(index - 1)))) {
2337        if ((_current != null) && (index >= 0)) _regions.add(index + 1, region);
2338        else _regions.add(region);
2339        
2340        _current = region;
2341        final int regionIndex = getIndexOf(region);
2342        final String JavaDoc stackTrace = StringOps.getStackTrace();
2343        
2344        // notify. invokeLater unnecessary if it only runs in the event thread
2345
Utilities.invokeLater(new Runnable JavaDoc() { public void run() {
2346          _lock.startRead();
2347          try {
2348            for (RegionManagerListener<R> l: _listeners) { l.regionAdded(region, regionIndex); }
2349          } finally { _lock.endRead(); }
2350        } });
2351        
2352        // remove region if necessary
2353
shrinkManager();
2354      }
2355      else {
2356        // if next was the region to be added, make that the current region
2357
if ((index < _regions.size()-1) && (region.equals(_regions.get(index+1)))) nextCurrentRegion();
2358        // if previous was the region to be added, make that the current region
2359
else if ((index > 0) && (region.equals(_regions.get(index-1)))) prevCurrentRegion();
2360      }
2361    }
2362    
2363    /** Remove regions more recent than the current region. */
2364    protected void removeMoreRecentThanCurrent() {
2365      if (_current != null) {
2366        int index = getIndexOf(_current);
2367        if (index < 0) return;
2368        while (index < _regions.size() - 1) { removeRegion(_regions.lastElement()); } // remove last element
2369
}
2370    }
2371    
2372    /** Remove regions if there are more than the maximum number allowed. Typically used to remove one region. */
2373    protected void shrinkManager() {
2374      if (_maxSize > 0) {
2375        int size;
2376        while ((size = _regions.size()) > _maxSize) {
2377          int index = getIndexOf(_current);
2378          if (index <= (size - 1)/2) {
2379            // in first half, remove last element; distinct from index unless _maxSize = 1
2380
_regions.remove(size - 1);
2381          }
2382          else {
2383            // in second half, remove first element; distinct from index
2384
_regions.remove(0);
2385          }
2386        }
2387      }
2388    }
2389
2390    /** Remove the given DocumentRegion from the manager.
2391     * @param region the DocumentRegion to be removed.
2392     */

2393    public void removeRegion(final R region) {
2394      // if we're removing the current region, select a more recent region, if available
2395
// if a more recent region is not available, select a less recent region, if available
2396
// if a less recent region is not available either, set to null
2397
final R cur = _current; // so we can verify if _current got changed
2398
if (region == cur) {
2399        if (nextCurrentRegion().equals(cur)) {
2400          if (prevCurrentRegion().equals(cur)) {
2401            _current = null;
2402          }
2403        }
2404      }
2405      for(int i = 0; i < _regions.size(); ++i) {
2406        if (region == _regions.get(i)) {
2407          _regions.remove(i);
2408          break;
2409        }
2410      }
2411      
2412      // notify
2413
Utilities.invokeLater(new Runnable JavaDoc() { public void run() {
2414        _lock.startRead();
2415        try {
2416          for (RegionManagerListener<R> l: _listeners) { l.regionRemoved (region); }
2417        } finally { _lock.endRead(); }
2418      } });
2419    }
2420    
2421    /** @return a Vector<R> containing the DocumentRegion objects in this mangager. */
2422    public Vector JavaDoc<R> getRegions() { return _regions; }
2423    
2424    /** Tells the manager to remove all regions. */
2425    public void clearRegions() {
2426      _current = null;
2427      while (_regions.size() > 0) { removeRegion(_regions.get(0)); }
2428    }
2429    
2430    /** @return the current region or null if none selected */
2431    public R getCurrentRegion() {
2432      if (! _regions.contains(_current)) _current = null;
2433      return _current;
2434    }
2435    
2436    /** @return the index of the current region or -1 if none selected */
2437    public int getCurrentRegionIndex() {
2438      if (_current != null) return getIndexOf(_current);
2439      return -1;
2440    }
2441    
2442    /** @return true if the current region is the first in the list, i.e. prevCurrentRegion is without effect */
2443    public boolean isCurrentRegionFirst() {
2444      return getIndexOf(_current) == 0;
2445    }
2446    
2447    /** @return true if the current region is the last in the list, i.e. nextCurrentRegion is without effect */
2448    public boolean isCurrentRegionLast() { return (getIndexOf(_current) == _regions.size() - 1); }
2449
2450    /** Set the current region.
2451     * @param region new current region */

2452    public void setCurrentRegion(R region) {
2453// if (!_regions.contains(_current)) _current = null;
2454
_current = region;
2455    }
2456    
2457    /** Make the region that is more recent the current region.
2458     * @return new current region */

2459    public R nextCurrentRegion() {
2460      if (_current != null) {
2461        int index = getIndexOf(_current);
2462        if (index + 1 < _regions.size()) _current = _regions.get(index+1);
2463      }
2464      else _current = _regions.lastElement();
2465      return _current;
2466    }
2467    
2468    /** Make the region that is less recent the current region.
2469     * @return new current region */

2470    public R prevCurrentRegion() {
2471      if (_current!=null) {
2472        int index = getIndexOf(_current);
2473        if (index-1 >= 0) {
2474          _current = _regions.get(index-1);
2475        }
2476      }
2477      else {
2478        _current = _regions.lastElement();
2479      }
2480      return _current;
2481    }
2482    
2483    /** Set the maximum number of regions that can be stored in this manager. If the maximum capacity has been reached
2484      * and another region is added, the region at the end farther away from the insertion location will be discarded.
2485      * @param size maximum number of regions, or 0 if no maximum
2486      */

2487    public void setMaximumSize(int size) {
2488      _maxSize = size;
2489      
2490      // remove regions if necessary
2491
shrinkManager();
2492    }
2493    
2494    /** @return the maximum number of regions that can be stored in this manager. */
2495    public int getMaximumSize() {
2496      return _maxSize;
2497    }
2498
2499    /** Apply the given command to the specified region to change it.
2500     * @param region the region to find and change
2501     * @param cmd command that mutates the region. */

2502    public void changeRegion(R region, Lambda<Object JavaDoc,R> cmd) {
2503      final int index = getIndexOf(region);
2504      if (index<0) { return; }
2505      final R r = _regions.get(index);
2506      cmd.apply(r);
2507      Utilities.invokeLater(new Runnable JavaDoc() { public void run() {
2508        // notify
2509
_lock.startRead();
2510        try {
2511          for (RegionManagerListener<R> l: _listeners) { l.regionChanged(r, index); }
2512        } finally { _lock.endRead(); }
2513      } });
2514    }
2515    
2516    /** Removes all listeners from this notifier. */
2517    public void removeAllListeners() {
2518      throw new UnsupportedOperationException JavaDoc("ConcreteRegionManager does not support removing all listeners");
2519      // this would be a potentially dangerous thing to do, as it would also remove the listeners that the subsets
2520
// have installed
2521
}
2522  }
2523
2524  /** Add the current location to the browser history. Aborts if not run in event thread. */
2525  public void addToBrowserHistory() {
2526    if (! EventQueue.isDispatchThread()) return;
2527    final OpenDefinitionsDocument doc = getActiveDocument();
2528    
2529    int startPos = 0; // required by javac
2530
int endPos = 0; // required by javac
2531
File JavaDoc file = null; // required by javac
2532

2533    if (doc != null) {
2534      try {
2535        startPos = doc.createPosition(doc.getCaretPosition()).getOffset();
2536        endPos = doc.createPosition(doc.getLineEndPos(doc.getCaretPosition())).getOffset();
2537        file = doc.getFile();
2538      }
2539      catch (FileMovedException fme) { /* ignore */ }
2540      catch (BadLocationException JavaDoc ble) { throw new UnexpectedException(ble); }
2541      
2542      getBrowserHistoryManager().addRegion(new SimpleDocumentRegion(doc, file, startPos, endPos));
2543    }
2544  }
2545  
2546  /** throws an UnsupportedOperationException */
2547  public ClassPathVector getClassPath() {
2548    throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support class paths");
2549  }
2550  
2551  public static boolean isUntitled(final File JavaDoc f) { return f == null || (f instanceof NullFile); }
2552  
2553  // ---------- ConcreteOpenDefDoc inner class ----------
2554

2555  /** A wrapper around a DefinitionsDocument or potential DefinitionsDocument (if it has been kicked out of the cache)
2556    * The GlobalModel interacts with DefinitionsDocuments through this wrapper.<br>
2557    * This call was formerly called the <code>DefinitionsDocumentHandler</code> but was renamed (2004-Jun-8) to be more
2558    * descriptive/intuitive. (Really? CC)
2559    */

2560  class ConcreteOpenDefDoc implements OpenDefinitionsDocument {
2561    protected class SubsetRegionManager<R extends DocumentRegion> extends EventNotifier<RegionManagerListener<R>>
2562      implements RegionManager<R> {
2563      
2564      /** The region manager it is a subset of. */
2565      private volatile RegionManager<R> _superSetManager;
2566      
2567      /** Creates a subset region manager that only sees the regions in this document. */
2568      public SubsetRegionManager(RegionManager<R> ssm) { _superSetManager = ssm; }
2569      
2570      /** @returns the superset manager. */
2571      public RegionManager<R> getSuperSetManager() { return _superSetManager; }
2572      
2573      /** Returns the region in this manager at the given offset, or null if one does not exist.
2574        * @param odd the document
2575        * @param offset the offset in the document
2576        * @return the DocumentRegion at the given line number, or null if it does not exist.
2577        */

2578      public R getRegionAt(OpenDefinitionsDocument odd, int offset) {
2579        return _superSetManager.getRegionAt(odd, offset);
2580      }
2581      
2582      /** Get the DocumentRegion that is stored in this RegionsTreePanel overlapping the area for the given document,
2583        * or null if it doesn't exist.
2584        * @param odd the document
2585        * @param startOffset the start offset
2586        * @param endOffset the end offset
2587        * @return the DocumentRegion or null
2588        */

2589      public R getRegionOverlapping(OpenDefinitionsDocument odd, int startOffset, int endOffset) {
2590        return _superSetManager.getRegionOverlapping(odd, startOffset, endOffset);
2591      }
2592      
2593      /** Add the supplied DocumentRegion to the manager.
2594       * @param region the DocumentRegion to be inserted into the manager
2595       */

2596      public void addRegion(R region) { _superSetManager.addRegion(region); }
2597      
2598      /** Remove the given DocumentRegion from the manager.
2599       * @param region the DocumentRegion to be removed.
2600       */

2601      public void removeRegion(R region) { _superSetManager.removeRegion(region); }
2602      
2603      /** @return a Vector<R> containing the DocumentRegion objects corresponding ONLY to this document. */
2604      public Vector JavaDoc<R> getRegions() {
2605        Vector JavaDoc<R> accum = new Vector JavaDoc<R>();
2606        Vector JavaDoc<R> regions = _superSetManager.getRegions();
2607        for (R r: regions) {
2608          if (r.getDocument().equals(ConcreteOpenDefDoc.this)) { accum.add(r); }
2609        }
2610        return accum;
2611      }
2612      
2613      /** Tells the manager to remove all regions corresponding ONLY to this document. */
2614      public void clearRegions() {
2615        Vector JavaDoc<R> regions = getRegions();
2616        for (R r: regions) {
2617          _superSetManager.removeRegion(r);
2618        }
2619      }
2620      
2621      /** Apply the given command to the specified region to change it.
2622       * @param region the region to find and change
2623       * @param cmd command that mutates the region. */

2624      public void changeRegion(R region, Lambda<Object JavaDoc,R> cmd) {
2625        _superSetManager.changeRegion(region, cmd);
2626      }
2627      
2628      /** A decorator to a RegionManagerListener that filters out everything but regions belonging to this document. */
2629      private class FilteredRegionManagerListener<R extends DocumentRegion> implements RegionManagerListener<R> {
2630        private RegionManagerListener<R> _decoree;
2631        public FilteredRegionManagerListener(RegionManagerListener<R> d) { _decoree = d; }
2632        public RegionManagerListener<R> getDecoree() { return _decoree; }
2633        public void regionAdded(R r, int index) { if (r.getDocument().equals(ConcreteOpenDefDoc.this)) { _decoree.regionAdded(r, index); } }
2634        public void regionChanged(R r, int index) { if (r.getDocument().equals(ConcreteOpenDefDoc.this)) { _decoree.regionChanged(r, index); } }
2635        public void regionRemoved(R r) { if (r.getDocument().equals(ConcreteOpenDefDoc.this)) { _decoree.regionRemoved(r); } }
2636      }
2637
2638      /** All filtered listeners that are listening to this subset. Accesses to this collection are protected by the
2639       * ReaderWriterLock. The collection must be synchronized, since multiple readers could access it at once.
2640       */

2641      protected final LinkedList JavaDoc<FilteredRegionManagerListener<R>> _filters = new LinkedList JavaDoc<FilteredRegionManagerListener<R>>();
2642    
2643      /** Provides synchronization primitives for solving the readers/writers problem. In EventNotifier, adding and
2644       * removing listeners are considered write operations, and all notifications are considered read operations. Multiple
2645       * reads can occur simultaneously, but only one write can occur at a time, and no reads can occur during a write.
2646       */

2647      protected final ReaderWriterLock _lock = new ReaderWriterLock();
2648    
2649      /** Adds a listener to the notifier.
2650       * @param listener a listener that reacts on events
2651       */

2652      public void addListener(RegionManagerListener<R> listener) {
2653        FilteredRegionManagerListener<R> filter = new FilteredRegionManagerListener<R>(listener);
2654        _lock.startWrite();
2655        try { _filters.add(filter); }
2656        finally {
2657          _lock.endWrite();
2658          _superSetManager.addListener(filter);
2659        }
2660      }
2661    
2662      /** Removes a listener from the notifier.
2663       * @param listener a listener that reacts on events
2664       */

2665      public void removeListener(RegionManagerListener<R> listener) {
2666        _lock.startWrite();
2667        try {
2668          for (FilteredRegionManagerListener<R> filter: _filters) {
2669            if (filter.getDecoree().equals(listener)) {
2670              _listeners.remove(filter);
2671              _superSetManager.removeListener(filter);
2672            }
2673          }
2674        }
2675        finally { _lock.endWrite(); }
2676      }
2677    
2678      /** Removes all listeners from this notifier. */
2679      public void removeAllListeners() {
2680        _lock.startWrite();
2681        try {
2682          for (FilteredRegionManagerListener<R> filter: _filters) {
2683            _listeners.remove(filter);
2684            _superSetManager.removeListener(filter);
2685          }
2686        }
2687        finally { _lock.endWrite(); }
2688      }
2689      
2690      /** @return the current region or null if none selected */
2691      public R getCurrentRegion() {
2692        // TODO
2693
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.getCurrentRegion not supported");
2694      }
2695      
2696      /** @return the index of the current region or -1 if none selected */
2697      public int getCurrentRegionIndex() {
2698        // TODO
2699
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.getCurrentRegionIndex not supported");
2700      }
2701      
2702      /** Set the current region.
2703       * @param region new current region */

2704      public void setCurrentRegion(R region) {
2705        // TODO
2706
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.setCurrentRegion not supported");
2707      }
2708      
2709      /** Make the region that is more recent the current region.
2710       * @return new current region */

2711      public R nextCurrentRegion() {
2712        // TODO
2713
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.nextCurrentRegion not supported");
2714      }
2715      
2716      /** Make the region that is less recent the current region.
2717       * @return new current region */

2718      public R prevCurrentRegion() {
2719        // TODO
2720
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.prevCurrentRegion not supported");
2721      }
2722      
2723      /** @return true if the current region is the first in the list, i.e. prevCurrentRegion is without effect */
2724      public boolean isCurrentRegionFirst() {
2725        // TODO
2726
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.isCurrentRegionFirst not supported");
2727      }
2728      
2729      /** @return true if the current region is the last in the list, i.e. nextCurrentRegion is without effect */
2730      public boolean isCurrentRegionLast() {
2731        // TODO
2732
throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.isCurrentRegionLast not supported");
2733      }
2734      
2735      /**
2736       * Set the maximum number of regions that can be stored in this manager.
2737       * If the maximum capacity has been reached and another region is added, the region at the end farther
2738       * away from the insertion location will be discarded.
2739       * @param size maximum number of regions, or 0 if no maximum
2740       */

2741      public void setMaximumSize(int size) {
2742        throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.setMaximumSize not supported");
2743      }
2744      
2745      /** @return the maximum number of regions that can be stored in this manager. */
2746      public int getMaximumSize() {
2747        throw new UnsupportedOperationException JavaDoc("SubsetRegionManager.getMaximumSize not supported");
2748      }
2749    }
2750    
2751// private boolean _modifiedSinceSave;
2752

2753    /** String image of document as last read from or written to disk; initially null */
2754    private byte[] _image;
2755    private volatile File JavaDoc _file;
2756    private volatile long _timestamp;
2757    
2758    /** Caret position, as set by the view. */
2759    private volatile int _caretPosition;
2760    
2761    /** The folder containing this document */
2762    private volatile File JavaDoc _parentDir;
2763    
2764    /** The package name embedded in the document the last time is was loaded, reconstructed, or saved. When loading a
2765     * project, this information is extracted from the project file eliminating the need to read every document file.
2766     * For non-project files, it is extracted from the text of the file. If there is an error, it is left as "".
2767     */

2768    protected volatile String JavaDoc _packageName = "";
2769    
2770    private volatile DCacheAdapter _cacheAdapter;
2771    
2772    /** Manager for bookmark regions. */
2773    protected final SubsetRegionManager<Breakpoint> _breakpointManager;
2774    
2775    /** Manager for bookmark regions. */
2776    protected final SubsetRegionManager<DocumentRegion> _bookmarkManager;
2777    
2778    /** Manager for find result regions. */
2779    protected final LinkedList JavaDoc<SubsetRegionManager<MovingDocumentRegion>> _findResultsManagers;
2780    
2781    /** Manager for browser history regions. */
2782    protected final SubsetRegionManager<DocumentRegion> _browserHistoryManager;
2783    
2784    private volatile int _initVScroll;
2785    private volatile int _initHScroll;
2786    private volatile int _initSelStart;
2787    private volatile int _initSelEnd;
2788    
2789    private volatile int _id;
2790    private volatile DrJavaBook _book;
2791
2792    /** Standard constructor for a document read from a file. Initializes this ODD's DD. Assumes that f exists.
2793     * @param f file describing DefinitionsDocument to manage; should be in canonical form
2794     */

2795    ConcreteOpenDefDoc(File JavaDoc f) { this(f, f.getParentFile(), f.lastModified()); }
2796    
2797     /* Standard constructor for a new document (no associated file). */
2798    ConcreteOpenDefDoc() { this(null, null, 0L); }
2799    
2800    /* General constructor. Only used privately. */
2801    private ConcreteOpenDefDoc(File JavaDoc f, File JavaDoc dir, long stamp) {
2802 
2803      _file = f;
2804      _parentDir = dir;
2805      _timestamp = stamp;
2806      _image = null;
2807      _id = ID_COUNTER++;
2808      
2809      try {
2810// System.out.println("about to make reconstructor " + this);
2811
DDReconstructor ddr = makeReconstructor();
2812// System.out.println("finished making reconstructor " + this);
2813
_cacheAdapter = _cache.register(this, ddr);
2814      } catch(IllegalStateException JavaDoc e) { throw new UnexpectedException(e); }
2815
2816      _breakpointManager = new SubsetRegionManager<Breakpoint>(AbstractGlobalModel.this.getBreakpointManager());
2817      _bookmarkManager = new SubsetRegionManager<DocumentRegion>(AbstractGlobalModel.this.getBookmarkManager());
2818      _findResultsManagers = new LinkedList JavaDoc<SubsetRegionManager<MovingDocumentRegion>>();
2819      for (RegionManager<MovingDocumentRegion> rm: AbstractGlobalModel.this.getFindResultsManagers ()) {
2820        addFindResultsManager(rm);
2821      }
2822      _browserHistoryManager = new SubsetRegionManager<DocumentRegion>(AbstractGlobalModel.this.getBrowserHistoryManager());
2823    }
2824    
2825    //------------ Getters and Setters -------------//
2826

2827    /** Returns the file field for this document; does not check whether the file is NullFile or file exists. */
2828    public File JavaDoc getRawFile() { return _file; }
2829    
2830    /** Returns the file for this document, null if the document is null (which should never happen). If the document's
2831     * file does not exist, this throws a FileMovedException. If a FileMovedException is thrown, you
2832     * can retrieve the non-existence source file from the FileMovedException by using the getFile() method.
2833     * @return the file for this document
2834     */

2835    public File JavaDoc getFile() throws FileMovedException {
2836      File JavaDoc f = _file; // single read of f
2837
if (AbstractGlobalModel.isUntitled(f)) return null; // assert f != null
2838
if (f.exists()) return f;
2839        else throw new FileMovedException(f, "This document's file has been moved or deleted.");
2840    }
2841    /** Sets the file for this openDefinitionsDocument. */
2842    public void setFile(final File JavaDoc file) {
2843      synchronized(this) { // ensures that _file and _timestamp are consistent
2844
_file = file;
2845        if (! AbstractGlobalModel.isUntitled(file)) _timestamp = file.lastModified();
2846        else _timestamp = 0L;
2847      }
2848    }
2849    
2850    /** Returns the timestamp. */
2851    public long getTimestamp() { return _timestamp; }
2852    
2853    /** Whenever this document has been saved, this method should be called to update its "isModified" information. */
2854    public void resetModification() {
2855      synchronized(this) {
2856        getDocument().resetModification();
2857        File JavaDoc f = _file;
2858        if (! AbstractGlobalModel.isUntitled(f)) _timestamp = f.lastModified();
2859      }
2860    }
2861    
2862    /** @return The parent directory; should be in canonical form. */
2863    public File JavaDoc getParentDirectory() { return _parentDir; }
2864    
2865    /** Sets the parent directory of the document only if it is "Untitled"
2866     * @param pd The parent directory
2867     */

2868    public void setParentDirectory(File JavaDoc pd) {
2869      synchronized(this) {
2870        if (! AbstractGlobalModel.isUntitled(_file))
2871          throw new IllegalArgumentException JavaDoc("The parent directory can only be set for untitled documents");
2872        _parentDir = pd;
2873      }
2874    }
2875 
2876    public int getInitialVerticalScroll() { return _initVScroll; }
2877    public int getInitialHorizontalScroll() { return _initHScroll; }
2878    public int getInitialSelectionStart() { return _initSelStart; }
2879    public int getInitialSelectionEnd() { return _initSelEnd; }
2880
2881    void setInitialVScroll(int i) { _initVScroll = i; }
2882    void setInitialHScroll(int i) { _initHScroll = i; }
2883    void setInitialSelStart(int i) { _initSelStart = i; }
2884    void setInitialSelEnd(int i) { _initSelEnd = i; }
2885  
2886    /** Gets the definitions document being handled.
2887     * @return document being handled
2888     */

2889    protected DefinitionsDocument getDocument() {
2890
2891// System.err.println("getDocument() called on " + this);
2892
try { return _cacheAdapter.getDocument(); }
2893      catch(IOException JavaDoc ioe) { // document has been moved or deleted
2894
// Utilities.showDebug("getDocument() failed for " + this);
2895
try {
2896          _notifier.documentNotFound(this, _file);
2897          final String JavaDoc path = fixPathForNavigator(getFile().getCanonicalFile().getCanonicalPath());
2898          Utilities.invokeLater(new SRunnable() { // formerly invokeAndWait(...) Why?
2899
public void run() { _documentNavigator.refreshDocument(ConcreteOpenDefDoc.this, path); }
2900          });
2901          return _cacheAdapter.getDocument();
2902        }
2903        catch(Throwable JavaDoc t) { throw new UnexpectedException(t); }
2904      }
2905    }
2906    
2907// /** Reconstructs the embedded positions for this document. */
2908
// public void makePositions() { _cacheAdapter.makePositions(); }
2909

2910    /** Returns the name of the top level class, if any.
2911     * @throws ClassNameNotFoundException if no top level class name found.
2912     */

2913    public String JavaDoc getFirstTopLevelClassName() throws ClassNameNotFoundException {
2914      return getDocument().getFirstTopLevelClassName();
2915    }
2916    
2917    /** Returns the name of the main (public) class, if any.
2918     * @throws ClassNameNotFoundException if no top level class name found.
2919     */

2920    public String JavaDoc getMainClassName() throws ClassNameNotFoundException {
2921      return getDocument().getMainClassName();
2922    }
2923    
2924    /** Returns the name of this file, or "(Untitled)" if no file. */
2925    public String JavaDoc getFileName() {
2926      if (isUntitled()) return "(Untitled)";
2927      return _file.getName();
2928    }
2929
2930    /** Returns the name of the file for this document with an appended asterisk (if modified) or spaces */
2931    public String JavaDoc getName() {
2932      String JavaDoc fileName = getFileName();
2933      if (isModifiedSinceSave()) fileName = fileName + "*";
2934      else fileName = fileName + " "; // forces the cell renderer to allocate space for an appended "*"
2935
return fileName;
2936    }
2937    
2938    /** Returns the canonical path for this document, "(Untitled)" if unsaved), "" if the file path is ill-formed. */
2939    public String JavaDoc getCanonicalPath() {
2940      if (isUntitled()) { return "(Untitled)"; }
2941      else { return IOUtil.attemptCanonicalFile(getRawFile()).getPath(); }
2942    }
2943     
2944    /** Returns the canonical path augmented by " *" if the document has been modified. */
2945    public String JavaDoc getCompletePath() {
2946      String JavaDoc path = getCanonicalPath();
2947      // Mark if modified
2948
if (isModifiedSinceSave()) path = path + " *";
2949      return path;
2950    }
2951    
2952    /** Finds the root directory for the source file for this document; null if document is Untitled.
2953     * @return The root directory of the source files, based on the package statement.
2954     * @throws InvalidPackageException if the package statement is invalid,
2955     * or if it does not match up with the location of the source file.
2956     */

2957    public File JavaDoc getSourceRoot() throws InvalidPackageException { return _getSourceRoot(_packageName); }
2958    
2959    /** @return the name of the package at the time of the most recent save or load operation. */
2960    public String JavaDoc getPackageName() { return _packageName; }
2961    
2962    /** Sets the cached _packageName for the preceding method. */
2963    public void setPackage(String JavaDoc name) { _packageName = name; }
2964    
2965    /** @return the name of the package currently embedded in document. */
2966    public String JavaDoc getPackageNameFromDocument() { return getDocument().getPackageName(); }
2967    
2968    
2969    /** Originally designed to allow undoManager to set the current document to be modified whenever an undo
2970     * or redo is performed. Now it actually does this.
2971     */

2972    public void updateModifiedSinceSave() { getDocument().updateModifiedSinceSave(); }
2973    
2974    /** Getter for document id; used to sort documents into creation order */
2975    public int id() { return _id; }
2976        
2977    /** Returns the Pageable object for printing.
2978     * @return A Pageable representing this document.
2979     */

2980    public Pageable JavaDoc getPageable() throws IllegalStateException JavaDoc { return _book; }
2981    
2982    /** Clears the pageable object used to hold the print job. */
2983    public void cleanUpPrintJob() { _book = null; }
2984
2985    //--------------- Simple Predicates ---------------//
2986

2987    /** A file is in the project if the source root is the same as the
2988     * project root. this means that project files must be saved at the
2989     * source root. (we query the model through the model's state)
2990     */

2991    public boolean inProjectPath() { return _state.inProjectPath(this); }
2992    
2993    /** An open file is in the new project if the source root is the same as the new project root. */
2994    public boolean inNewProjectPath(File JavaDoc projRoot) {
2995      try { return ! isUntitled() && IOUtil.isMember(getFile(), projRoot); }
2996      catch(FileMovedException e) { return false; }
2997    }
2998 
2999    /** A file is in the project if it is explicitly listed as part of the project. */
3000    public boolean inProject() { return ! isUntitled() && _state.inProject(_file); }
3001    
3002    /** @return true if this is an auxiliary file. */
3003    public boolean isAuxiliaryFile() { return ! isUntitled() && _state.isAuxiliaryFile(_file); }
3004    
3005    /** @return true if this has a legal source file name (ends in extension ".java", ".dj0", ".dj1", or ".dj2". */
3006    public boolean isSourceFile() {
3007      if (isUntitled()) return false; // assert _file != null
3008
String JavaDoc name = _file.getName();
3009      for (String JavaDoc ext: CompilerModel.EXTENSIONS) { if (name.endsWith(ext)) return true; }
3010      return false;
3011    }
3012     
3013    /** Returns whether this document is currently untitled (indicating whether it has a file yet or not).
3014     * @return true if the document is untitled and has no file
3015     */

3016    public boolean isUntitled() { return AbstractGlobalModel.isUntitled(_file); }
3017    
3018    public boolean isUntitledAndEmpty() { return isUntitled() && getLength() == 0; } // should be synchronized?
3019

3020    /** Returns true if the file exists on disk. Returns false if the file has been moved or deleted */
3021    public boolean fileExists() {
3022      File JavaDoc f = _file; // single read of _file;
3023
return ! AbstractGlobalModel.isUntitled(f) && f.exists();
3024    }
3025    
3026    //--------------- Major Operations ----------------//
3027

3028    /** Returns true if the file exists on disk. Prompts the user otherwise */
3029    public boolean verifyExists() {
3030// Utilities.showDebug("verifyExists called on " + _file);
3031
if (fileExists()) return true;
3032      //prompt the user to find it
3033
try {
3034        _notifier.documentNotFound(this, _file);
3035        File JavaDoc f = getFile();
3036        if (isUntitled()) return false;
3037        String JavaDoc path = fixPathForNavigator(getFile().getCanonicalPath());
3038        _documentNavigator.refreshDocument(this, path);
3039        return true;
3040      }
3041      catch(Throwable JavaDoc t) { return false; }
3042// catch(DocumentFileClosed e) { /* not clear what to do here */ }
3043
}
3044    
3045    /** Makes a default DDReconstructor that will make the corresponding DefinitionsDocument. */
3046    protected DDReconstructor makeReconstructor() {
3047      return new DDReconstructor() {
3048        
3049        // Brand New documents start at location 0
3050
private volatile int _loc = 0;
3051        
3052        // Start out with empty lists of listeners on the very first time the document is made
3053
private volatile DocumentListener JavaDoc[] _list = { };
3054        private volatile List JavaDoc<FinalizationListener<DefinitionsDocument>> _finalListeners =
3055          new LinkedList JavaDoc<FinalizationListener<DefinitionsDocument>>();
3056        
3057        // Weak hashmap that associates a WrappedPosition with its offset when saveDocInfo was called
3058
private volatile WeakHashMap JavaDoc< DefinitionsDocument.WrappedPosition, Integer JavaDoc> _positions =
3059          new WeakHashMap JavaDoc<DefinitionsDocument.WrappedPosition, Integer JavaDoc>();
3060        
3061        public DefinitionsDocument make() throws IOException JavaDoc, BadLocationException JavaDoc, FileMovedException {
3062          
3063// System.err.println("DDReconstructor.make() called on " + ConcreteOpenDefDoc.this);
3064
DefinitionsDocument newDefDoc = new DefinitionsDocument(_notifier);
3065          newDefDoc.setOpenDefDoc(ConcreteOpenDefDoc.this);
3066          
3067          if (_image != null) {
3068            _editorKit.read(new InputStreamReader JavaDoc(new ByteArrayInputStream JavaDoc(_image)), newDefDoc, 0);
3069            _log.log("Reading from image for " + _file + " containing " + _image.length + " chars");
3070          }
3071          else if (! isUntitled()) {
3072            final InputStreamReader JavaDoc reader = new FileReader JavaDoc(_file);
3073            _editorKit.read(reader, newDefDoc, 0);
3074            reader.close(); // win32 needs readers closed explicitly!
3075
}
3076          _loc = Math.min(_loc, newDefDoc.getLength()); // make sure not past end
3077
_loc = Math.max(_loc, 0); // make sure not less than 0
3078
newDefDoc.setCurrentLocation(_loc);
3079          for (DocumentListener JavaDoc d : _list) {
3080            if (d instanceof DocumentUIListener) newDefDoc.addDocumentListener(d);
3081          }
3082          for (FinalizationListener<DefinitionsDocument> l: _finalListeners) {
3083            newDefDoc.addFinalizationListener(l);
3084          }
3085
3086          // re-create and update all positions
3087
newDefDoc.setWrappedPositionOffsets(_positions);
3088          
3089          newDefDoc.resetModification(); // Why is this necessary? A reconstructed document is already unmodified.
3090

3091          // tempDoc.setUndoManager(_undo);
3092
assert ! newDefDoc.isModifiedSinceSave();
3093// System.err.println ("_packageName in make() = " + _packageName);
3094
// System.err.println("tempDoc.getLength() = " + tempDoc.getLength());
3095
_packageName = newDefDoc.getPackageName();
3096// System.err.println("make() returned " + newDefDoc);
3097
return newDefDoc;
3098        }
3099        
3100        
3101// private volatile boolean _positionsMade = false;
3102
//
3103
// /** Reconstructs this document except for embedded positions. Assumes _cacheLock is held.
3104
// * @param image the bytes in the cached file image for this document.
3105
// */
3106
// public DefinitionsDocument make() throws IOException, BadLocationException, FileMovedException { // should sync on _file
3107
//
3108
//// System.err.println("DDReconstructor.make() called on " + ConcreteOpenDefDoc.this);
3109
// DefinitionsDocument newDefDoc = new DefinitionsDocument(_notifier);
3110
// newDefDoc.setOpenDefDoc(ConcreteOpenDefDoc.this);
3111
//
3112
// if (_image != null) {
3113
// _editorKit.read(new InputStreamReader(new ByteArrayInputStream(_image)), newDefDoc, 0);
3114
// _log.log("Reading from image for " + _file + " containing " + _image.length + " chars");
3115
// }
3116
// else if (! isUntitled()) {
3117
// final InputStreamReader reader = new FileReader(_file);
3118
// _editorKit.read(reader, newDefDoc, 0);
3119
// reader.close(); // win32 needs readers closed explicitly!
3120
// }
3121
// _loc = Math.min(_loc, newDefDoc.getLength()); // make sure not past end
3122
// _loc = Math.max(_loc, 0); // make sure not less than 0
3123
// newDefDoc.setCurrentLocation(_loc);
3124
// for (DocumentListener d : _list) {
3125
// if (d instanceof DocumentUIListener) newDefDoc.addDocumentListener(d);
3126
// }
3127
// for (FinalizationListener<DefinitionsDocument> l: _finalListeners) {
3128
// newDefDoc.addFinalizationListener(l);
3129
// }
3130
//
3131
// _positionsMade = false;
3132
//
3133
// newDefDoc.resetModification(); // Why is this necessary? A reconstructed document is already unmodified.
3134
//
3135
// // tempDoc.setUndoManager(_undo);
3136
// assert ! newDefDoc.isModifiedSinceSave();
3137
//// System.err.println ("_packageName in make() = " + _packageName);
3138
//// System.err.println("tempDoc.getLength() = " + tempDoc.getLength());
3139
// _packageName = newDefDoc.getPackageName();
3140
//// System.err.println("make() returned " + newDefDoc);
3141
// return newDefDoc;
3142
//
3143
// }
3144

3145// /** Reconstructs the embedded positions for this document. Synchronized*/
3146
// public void makePositions() {
3147
// if (_positionsMade) return;
3148
// synchronized(this) {
3149
// if (_positionsMade) return; // double-check works for volatile fields in Java 1.4 and later code
3150
// _positionsMade = true;
3151
// }
3152
// try { getDocument().setWrappedPositionOffsets(_positions); }
3153
// catch(Exception e) { /* ignore */ } // omitted positions are not fatal
3154
// }
3155

3156        /** Saves the information for this document before it is kicked out of the cache. Only called from
3157          * DocumentCache. Assumes that cache lock is already held.
3158          */

3159        public void saveDocInfo(DefinitionsDocument doc) {
3160// These lines were commented out to fix a memory leak; evidently, the undomanager holds on to the document
3161
// _undo = doc.getUndoManager();
3162
// _undoListeners = doc.getUndoableEditListeners();
3163
// Save document image. Note: this could be optimized to eliminate redundant updates to _image
3164
String JavaDoc text = doc.getText();
3165          if (text.length() > 0) {
3166            _image = text.getBytes();
3167            _log.log("Saving image containing " + _image.length + " chars for " + _file);
3168          }
3169          _loc = doc.getCurrentLocation();
3170          _list = doc.getDocumentListeners();
3171          _finalListeners = doc.getFinalizationListeners ();
3172          
3173          // save offsets of all positions
3174
_positions.clear();
3175          _positions = doc.getWrappedPositionOffsets();
3176        }
3177        
3178        public void addDocumentListener(DocumentListener JavaDoc dl) {
3179          ArrayList JavaDoc<DocumentListener JavaDoc> tmp = new ArrayList JavaDoc<DocumentListener JavaDoc>();
3180          for (DocumentListener JavaDoc l: _list) { if (dl != l) tmp.add(l); }
3181          tmp.add(dl);
3182          _list = tmp.toArray (new DocumentListener JavaDoc[tmp.size()]);
3183        }
3184        public String JavaDoc toString() { return ConcreteOpenDefDoc.this.toString(); }
3185      };
3186    }
3187
3188    /** Saves the document with a FileWriter. If the file name is already set, the method will use
3189     * that name instead of whatever selector is passed in.
3190     * @param com a selector that picks the file name if the doc is untitled
3191     * @exception IOException
3192     * @return true if the file was saved, false if the operation was canceled
3193     */

3194    public boolean saveFile(FileSaveSelector com) throws IOException JavaDoc {
3195// System.err.println("saveFile called on " + this);
3196
// Update value of _packageName since modification flag will be set to false
3197
if (! isModifiedSinceSave()) return true;
3198      if (isUntitled()) return saveFileAs(com);
3199      
3200      // Didn't need to save since file is named and unmodified; return true, since the save wasn't "canceled"
3201

3202// System.err.println("Saving file: " + getFile());
3203

3204      // Update package name by parsing file
3205
_packageName = getDocument().getPackageName();
3206      FileSaveSelector realCommand = com;
3207      try {
3208        final File JavaDoc file = getFile();
3209// System.err.println("file name for doc to be saved is: " + file);
3210
if (! isUntitled()) {
3211          realCommand = new TrivialFSS(file);
3212// System.err.println("TrivialFSS set up");
3213
}
3214      }
3215      catch (FileMovedException fme) {
3216        // getFile() failed, prompt the user if a new one should be selected
3217
if ( com.shouldSaveAfterFileMoved(this, fme.getFile())) realCommand = com;
3218        else return false;
3219        // User declines to save as a new file, so don't save
3220
}
3221// System.err.println("Calling saveFileAs");
3222
return saveFileAs(realCommand);
3223    }
3224
3225    /** Saves the document with a FileWriter. The FileSaveSelector will either provide a file name or prompt the
3226      * user for one. It is up to the caller to decide what needs to be done to choose a file to save to. Once
3227      * the file has been saved succssfully, this method fires fileSave(File). If the save fails for any
3228      * reason, the event is not fired. This is synchronized against the compiler model to prevent saving and
3229      * compiling at the same time- this used to freeze drjava.
3230      * @param com a selector that picks the file name.
3231      * @throws IOException if the save fails due to an IO error
3232      * @return true if the file was saved, false if the operation was canceled
3233      */

3234    public boolean saveFileAs(FileSaveSelector com) throws IOException JavaDoc {
3235      // Update _packageName since modifiedSinceSaved flag will be set to false
3236
_packageName = getDocument().getPackageName();
3237      try {
3238        final OpenDefinitionsDocument openDoc = this;
3239        final File JavaDoc file = com.getFile().getCanonicalFile();
3240        _log.log("saveFileAs called on " + file);
3241        OpenDefinitionsDocument otherDoc = _getOpenDocument(file);
3242
3243        // Check if file is already open in another document
3244
boolean openInOtherDoc = ((otherDoc != null) && (openDoc != otherDoc));
3245        
3246        // If the file is open in another document, abort if user does not confirm overwriting it
3247
if (openInOtherDoc) {
3248          boolean shouldOverwrite = com.warnFileOpen(file);
3249          if (! shouldOverwrite) return true; // operation not cancelled? Strange
3250
}
3251        
3252        if (! file.exists() || com.verifyOverwrite()) { // confirm that existing file can be overwritten
3253

3254// System.err.println("Writing file " + file);
3255

3256          // Correct the case of the filename (in Windows)
3257
if (! file.getCanonicalFile().getName().equals(file.getName())) file.renameTo(file);
3258          
3259          // Check for # in the path of the file because if there
3260
// is one, then the file cannot be used in the Interactions Pane
3261
if (file.getAbsolutePath().indexOf("#") != -1) _notifier.filePathContainsPound();
3262          
3263          // have FileOps save the file
3264
// System.err.println("Calling FileOps.saveFile to save it");
3265
FileOps.saveFile(new FileOps.DefaultFileSaver(file) {
3266            /** Only runs in event thread so no read lock is necessary. */
3267            public void saveTo(OutputStream JavaDoc os) throws IOException JavaDoc {
3268              DefinitionsDocument dd = getDocument();
3269              try {
3270                _editorKit.write(os, dd, 0, dd.getLength());
3271// Utilities.show ("Wrote file containing:\n" + doc.getText());
3272
}
3273              catch (BadLocationException JavaDoc docFailed) { throw new UnexpectedException(docFailed); }
3274            }
3275          });
3276          resetModification();
3277          synchronized(_documentsRepos) {
3278            File JavaDoc f = getRawFile();
3279// OpenDefinitionsDocument d = _documentsRepos.get(f);
3280
// d == this except in some unit tests where documents are not entered in _documentRepos
3281
// assert d == this;
3282
_documentsRepos.remove(f);
3283            _documentsRepos.put(file, this);
3284          }
3285          setFile(file);
3286          
3287          
3288          // this.getPackageName does not return "" if this is untitled and contains a legal package declaration
3289
// try {
3290
// // This calls getDocument().getPackageName() because this may be untitled and this.getPackageName()
3291
// // returns "" if it's untitled. Right here we are interested in parsing the DefinitionsDocument's text
3292
// _packageName = getDocument().getPackageName();
3293
// }
3294
// catch(InvalidPackageException e) { _packageName = null; }
3295
getDocument().setCachedClassFile(null);
3296          checkIfClassFileInSync();
3297          
3298// Utilities.showDebug("ready to fire fileSaved for " + this);
3299
_notifier.fileSaved(openDoc);
3300          
3301          // Make sure this file is on the appropriate classpaths (does nothing in AbstractGlobalModel)
3302
addDocToClassPath(this);
3303          
3304          /* update the navigator */
3305          _documentNavigator.refreshDocument(this, fixPathForNavigator(file.getCanonicalPath()));
3306          
3307          /* set project changed flag */
3308          setProjectChanged(true);
3309        }
3310        return true;
3311      }
3312      catch (OperationCanceledException oce) {
3313        // Thrown by com.getFile() if the user cancels.
3314
// We don't save if this happens.
3315
return false;
3316      }
3317    }
3318    
3319 
3320    /** This method tells the document to prepare all the DrJavaBook and PagePrinter objects. */
3321    public void preparePrintJob() throws BadLocationException JavaDoc, FileMovedException {
3322      String JavaDoc fileName = "(Untitled)";
3323      File JavaDoc sourceFile = getFile(); // single read of _file
3324
if (! AbstractGlobalModel.isUntitled(sourceFile)) fileName = sourceFile.getAbsolutePath();
3325
3326      _book = new DrJavaBook(getDocument().getText(), fileName, _pageFormat);
3327    }
3328
3329    /** Prints the given document by bringing up a "Print" window. */
3330    public void print() throws PrinterException JavaDoc, BadLocationException JavaDoc, FileMovedException {
3331      preparePrintJob();
3332      PrinterJob JavaDoc printJob = PrinterJob.getPrinterJob();
3333      printJob.setPageable(_book);
3334      if (printJob.printDialog()) printJob.print();
3335      cleanUpPrintJob();
3336    }
3337
3338
3339    /** throws UnsupportedOperationException */
3340    public void startCompile() throws IOException JavaDoc {
3341      throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support compilation");
3342    }
3343
3344    /** throws UnsupportedOperationException */
3345    public void runMain() throws IOException JavaDoc, ClassNameNotFoundException {
3346      throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support running");
3347    }
3348
3349    /** throws UnsupportedOperationException */
3350    public void startJUnit() throws IOException JavaDoc, ClassNotFoundException JavaDoc {
3351      throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support unit testing");
3352    }
3353
3354    /** throws UnsupportedOperationException */
3355    public void generateJavadoc(FileSaveSelector saver) throws IOException JavaDoc {
3356      throw new UnsupportedOperationException JavaDoc("AbstractGlobalModel does not support javadoc");
3357    }
3358    
3359    /** Determines if the document has been modified since the last save.
3360     * @return true if the document has been modified
3361     */

3362    public boolean isModifiedSinceSave() {
3363      /* If the document has not been registered or it is virtualized (only stored on disk), then we know that
3364       * it is not modified. This method can be called by debugging code (via getName() on a
3365       * ConcreteOpenDefDoc) before the document has been registered (_cacheAdapter == null). */

3366      if (_cacheAdapter != null && _cacheAdapter.isReady()) return getDocument().isModifiedSinceSave();
3367      else return false;
3368    }
3369    
3370    public void documentSaved() { _cacheAdapter.documentSaved(getFileName()); }
3371    
3372    public void documentModified() { _cacheAdapter.documentModified(); }
3373    
3374    public void documentReset() { _cacheAdapter.documentReset(); }
3375    
3376    /** Determines if the file for this document has been modified since it was loaded.
3377     * @return true if the file has been modified
3378     */

3379    public boolean modifiedOnDisk() {
3380      boolean ret = false;
3381      final File JavaDoc f = _file; // single read of f
3382
if (! AbstractGlobalModel.isUntitled(f)) ret = (f.lastModified() > _timestamp);
3383      return ret;
3384    }
3385    
3386    /** Determines if document has a class file consistent with its current state. If this document is unmodified,
3387     * this method examines the primary class file corresponding to this document and compares the timestamps of
3388     * the class file to that of the source file. An empty untitled document is consider to be "in sync".
3389     */

3390    public boolean checkIfClassFileInSync() {
3391      _log.log("checkIfClassFileInSync() called for " + this);
3392      if (isUntitled()) return true; // unmodified, untitled document
3393

3394      // If modified, then definitely out of sync
3395
DefinitionsDocument dd = getDocument();
3396      if (isModifiedSinceSave()) {
3397        dd.setClassFileInSync(false);
3398// _log.log("checkIfClassFileInSync = false because isModifiedSinceSave()");
3399
return false;
3400      }
3401      
3402      // Look for cached class file
3403
File JavaDoc classFile = dd.getCachedClassFile();
3404// _log.log("In checkIfClassFileInSync cacched value of classFile = " + classFile);
3405
if (classFile == null) {
3406        // Not cached, so locate the file
3407
classFile = _locateClassFile();
3408// _log.log(this + ": in checkIfClassFileInSync _locateClassFile() = " + classFile);
3409
dd.setCachedClassFile(classFile);
3410        if ((classFile == null) || (! classFile.exists())) {
3411          // couldn't find the class file
3412
// _log.log(this + ": Could not find class file");
3413
dd.setClassFileInSync(false);
3414          return false;
3415        }
3416      }
3417
3418      // compare timestamps
3419

3420      File JavaDoc sourceFile;
3421      try { sourceFile = getFile(); }
3422      catch (FileMovedException fme) {
3423        dd.setClassFileInSync(false);
3424// _log.log(this + ": File moved");
3425
return false;
3426      }
3427      if (sourceFile != null) {
3428        _log.log(sourceFile + " has timestamp " + sourceFile.lastModified());
3429        _log.log(classFile + " has timestamp " + classFile.lastModified());
3430      }
3431      if (sourceFile == null || sourceFile.lastModified() > classFile.lastModified()) { // assert sourceFile != null
3432
dd.setClassFileInSync(false);
3433// _log.log(this + ": date stamps indicate modification");
3434
return false;
3435      }
3436      else {
3437        dd.setClassFileInSync (true);
3438        return true;
3439      }
3440    }
3441
3442    /** Returns the class file for this source document by searching the source roots of open documents, the
3443     * system classpath, and the "extra.classpath ". Returns null if the class file could not be found.
3444     */

3445    private File JavaDoc _locateClassFile() {
3446      if (isUntitled()) return null;
3447      
3448      String JavaDoc className;
3449      try { className = getDocument().getQualifiedClassName(); }
3450      catch (ClassNameNotFoundException cnnfe) {
3451        _log.log("_locateClassFile() failed for " + this + " because getQualifedClassName returned ClassNotFound");
3452        return null; /* No source class name */
3453      }
3454      _log.log("In _locateClassFile, className = " + className);
3455      String JavaDoc ps = System.getProperty("file.separator");
3456      // replace periods with the System's file separator
3457
className = StringOps.replace(className, ".", ps);
3458      String JavaDoc fileName = className + ".class";
3459      
3460      _log.log("In _locateClassFile, classfileName = " + fileName);
3461      
3462      // Check source root set (open files)
3463
ArrayList JavaDoc<File JavaDoc> roots = new ArrayList JavaDoc<File JavaDoc>();
3464      
3465      if (getBuildDirectory() != null) roots.add(getBuildDirectory());
3466      
3467      // Add the current document to the beginning of the roots list
3468
try {
3469        File JavaDoc root = getSourceRoot();
3470        _log.log("Directory " + root + " added to list of source roots");
3471        roots.add(root);
3472      }
3473      catch (InvalidPackageException ipe) {
3474        try {
3475          _log.log(this + " has no source root, using parent directory instead");
3476          File JavaDoc root = getFile().getParentFile();
3477          if (root != null) {
3478            roots.add(root);
3479            _log.log("Added parent directory " + root + " to list of source roots");
3480          }
3481        }
3482        catch(NullPointerException JavaDoc e) { throw new UnexpectedException(e); }
3483        catch(FileMovedException fme) {
3484          // Moved, but we'll add the old file to the set anyway
3485
_log.log("File for " + this + "has moved; adding parent directory to list of roots");
3486          File JavaDoc root = fme.getFile().getParentFile();
3487          if (root != null) roots.add(root);
3488        }
3489      }
3490      
3491      File JavaDoc classFile = findFileInPaths(fileName, roots);
3492      if (classFile != null) {
3493        _log.log("Found source file " + classFile + " for " + this);
3494        return classFile;
3495      }
3496      
3497      _log.log(this + " not found on path of source roots");
3498      // Class not on source root set, check system classpath
3499
String JavaDoc cp = System.getProperty("java.class.path");
3500      String JavaDoc pathSeparator = System.getProperty("path.separator");
3501      Vector JavaDoc<File JavaDoc> cpVector = new Vector JavaDoc<File JavaDoc>();
3502      int i = 0;
3503      while (i < cp.length()) {
3504        int nextSeparator = cp.indexOf(pathSeparator, i);
3505        if (nextSeparator == -1) {
3506          cpVector.add(new File JavaDoc(cp.substring(i, cp.length())));
3507          break;
3508        }
3509        cpVector.add(new File JavaDoc(cp.substring(i, nextSeparator)));
3510        i = nextSeparator + 1;
3511      }
3512      classFile = findFileInPaths(fileName, cpVector);
3513      
3514      if (classFile != null) return classFile;
3515      
3516      // not on system classpath, check interactions classpath
3517
return findFileInPaths(fileName, DrJava.getConfig().getSetting(EXTRA_CLASSPATH));
3518    }
3519
3520    /** Determines if the definitions document has been changed by an outside agent. If the document has changed,
3521     * asks the listeners if the GlobalModel should revert the document to the most recent version saved.
3522     * @return true if document has been reverted
3523     */

3524    public boolean revertIfModifiedOnDisk() throws IOException JavaDoc{
3525      final OpenDefinitionsDocument doc = this;
3526      if (modifiedOnDisk()) {
3527        boolean shouldRevert = _notifier.shouldRevertFile(doc);
3528        if (shouldRevert) doc.revertFile();
3529        return shouldRevert;
3530      }
3531      return false;
3532    }
3533    
3534    /** Degenerate version of close; does not remove breakpoints in this document */
3535    public void close() {
3536      removeFromDebugger();
3537      _cacheAdapter.close();
3538    }
3539
3540    /** Reverts current ODD to file content on disk. */
3541    public void revertFile() throws IOException JavaDoc {
3542      
3543      final OpenDefinitionsDocument doc = this;
3544      
3545      //need to remove old, possibly invalid breakpoints
3546
removeFromDebugger();
3547      doc.getBreakpointManager().clearRegions();
3548      doc.getBookmarkManager().clearRegions();
3549      for (RegionManager<MovingDocumentRegion> rm: doc.getFindResultsManagers()) rm.clearRegions();
3550      doc.getBrowserHistoryManager().clearRegions();
3551      
3552      if (doc.isUntitled()) throw new UnexpectedException("Cannot revert an Untitled file!");
3553      
3554      try {
3555        //this line precedes .remove() so that an invalid file is not cleared before this fact is discovered.
3556
File JavaDoc file = doc.getFile();
3557        FileReader JavaDoc reader = new FileReader JavaDoc(file);
3558        doc.clear();
3559
3560        _editorKit.read(reader, doc, 0);
3561        reader.close(); // win32 needs readers closed explicitly!
3562

3563        resetModification();
3564        doc.checkIfClassFileInSync();
3565        setCurrentLocation(0);
3566        _notifier.fileReverted(doc);
3567      }
3568      catch (BadLocationException JavaDoc e) { throw new UnexpectedException(e); }
3569    }
3570
3571    /** Asks the listeners if the GlobalModel can abandon the current document. Fires the canAbandonFile(File)
3572     * event if isModifiedSinceSave() is true. Only executes in event thread except for tests.
3573     * @return true if the current document can be abandoned, false if the current action should be halted in
3574     * its tracks (e.g., file open when the document has been modified since the last save).
3575     */

3576    public boolean canAbandonFile() {
3577// assert EventQueue.isDispatchThread();
3578
if (isUntitledAndEmpty()) return true;
3579      File JavaDoc f = _file;
3580      if (isModifiedSinceSave() || (! AbstractGlobalModel.isUntitled(f) && ! f.exists() && _cacheAdapter.isReady()))
3581        return _notifier.canAbandonFile(this);
3582      else return true;
3583    }
3584    
3585    /** Fires the quit(File) event if isModifiedSinceSave() is true. The quitFile() event asks the user if the
3586     * the file should be saved before quitting. Only executes in event thread.
3587     * @return true if quitting should continue, false if the user cancelled
3588     */

3589    public boolean quitFile() {
3590      assert EventQueue.isDispatchThread();
3591      File JavaDoc f = _file;
3592      if (isModifiedSinceSave() || (f != null && ! f.exists() && _cacheAdapter.isReady())) return _notifier.quitFile(this);
3593      return true;
3594    }
3595    
3596    /** Moves the definitions document to the given line, and returns the resulting character position.
3597     * @param line Destination line number. If it exceeds the number of lines in the document, it is
3598     * interpreted as the last line.
3599     * @return Index into document of where it moved
3600     */

3601    public int gotoLine(int line) {
3602      DefinitionsDocument dd = getDocument();
3603      dd.gotoLine(line);
3604      return dd.getCurrentLocation();
3605    }
3606
3607    /** Forwarding method to sync the definitions with whatever view component is representing them. */
3608    public void setCurrentLocation(int location) { _caretPosition = location; getDocument().setCurrentLocation(location); }
3609
3610    /** Get the location of the cursor in the definitions according to the definitions document. */
3611    public int getCurrentLocation() { return getDocument().getCurrentLocation(); }
3612    
3613    /** @return the caret position as set by the view. */
3614    public int getCaretPosition() { return _caretPosition; }
3615
3616    /** Forwarding method to find the match for the closing brace immediately to the left, assuming there is such a brace.
3617     * @return the relative distance backwards to the offset before the matching brace.
3618     */

3619    public int balanceBackward() { return getDocument().balanceBackward(); }
3620
3621    /** Forwarding method to find the match for the open brace immediately to the right, assuming there is such a brace.
3622     * @return the relative distance forwards to the offset after `the matching brace.
3623     */

3624    public int balanceForward() { return getDocument().balanceForward(); }
3625    
3626    /** @return the breakpoint region manager. */
3627    public RegionManager<Breakpoint> getBreakpointManager() { return _breakpointManager; }
3628    
3629    /** @return the bookmark region manager. */
3630    public RegionManager<DocumentRegion> getBookmarkManager() { return _bookmarkManager; }
3631    
3632    /** @return the find result region managers. */
3633    public List JavaDoc<RegionManager<MovingDocumentRegion>> getFindResultsManagers() {
3634      LinkedList JavaDoc<RegionManager<MovingDocumentRegion>> newList = new LinkedList JavaDoc<RegionManager<MovingDocumentRegion>>();
3635      for (SubsetRegionManager<MovingDocumentRegion> rm: _findResultsManagers) { newList.add (rm); }
3636      return newList;
3637    }
3638    
3639    /** Add a region manager for find results to this document.
3640     * @param rm the global model's region manager */

3641    public void addFindResultsManager(RegionManager<MovingDocumentRegion> rm) {
3642      _findResultsManagers.add(new SubsetRegionManager<MovingDocumentRegion>(rm));
3643    }
3644    
3645    /** Remove a manager for find results from this document.
3646     * @param rm the global model's region manager. */

3647    public void removeFindResultsManager(RegionManager<MovingDocumentRegion> rm) {
3648      for (SubsetRegionManager<MovingDocumentRegion> ssrm: _findResultsManagers) {
3649        if (ssrm.getSuperSetManager().equals(rm)) {
3650          _findResultsManagers.remove(ssrm);
3651          break;
3652        }
3653      }
3654    }
3655    
3656    /** @return manager for browser history regions for this document. */
3657    public RegionManager<DocumentRegion> getBrowserHistoryManager() { return _browserHistoryManager; }
3658
3659    /** throws UnsupportedOperationException */
3660    public void removeFromDebugger() { /* do nothing because it is called in methods in this class */ }
3661
3662    /** Finds the root directory of the source file.
3663     * @param packageName Package name, already fetched from the document
3664     * @return The root directory of the source file based on the package statement.
3665     * @throws InvalidPackageException If the package statement is invalid, or if it does not match up with the
3666     * location of the source file.
3667     */

3668    File JavaDoc _getSourceRoot(String JavaDoc packageName) throws InvalidPackageException {
3669      
3670      if (isUntitled())
3671        throw new InvalidPackageException(-1, "Can not get source root for unsaved file. Please save.");
3672      
3673      File JavaDoc sourceFile;
3674      try { sourceFile = getFile(); }
3675      catch (FileMovedException fme) {
3676        throw new
3677          InvalidPackageException(-1, "File has been moved or deleted from its previous location. Please save.");
3678      }
3679      
3680      if (packageName.equals("")) { return sourceFile.getParentFile(); }
3681      
3682      ArrayList JavaDoc<String JavaDoc> packageStack = new ArrayList JavaDoc<String JavaDoc>();
3683      int dotIndex = packageName.indexOf('.');
3684      int curPartBegins = 0;
3685      
3686      while (dotIndex != -1) {
3687        packageStack.add(packageName.substring(curPartBegins, dotIndex));
3688        curPartBegins = dotIndex + 1;
3689        dotIndex = packageName.indexOf ('.', dotIndex + 1);
3690      }
3691      
3692      // Now add the last package component
3693
packageStack.add(packageName.substring(curPartBegins));
3694      
3695      // Must use the canonical path, in case there are dots in the path (which will conflict with the package name)
3696
try {
3697        File JavaDoc parentDir = sourceFile.getCanonicalFile();
3698        File JavaDoc grandParentDir;
3699        while (! packageStack.isEmpty()) {
3700          String JavaDoc part = pop(packageStack);
3701          parentDir = parentDir.getParentFile();
3702          grandParentDir = parentDir.getParentFile();
3703
3704          if (parentDir == null) throw new RuntimeException JavaDoc("parent dir is null!");
3705
3706          // Make sure the package piece matches the directory name
3707
boolean equal;
3708          if (grandParentDir!=null) {
3709            // grand parent exists, compare File objects
3710
// this handles case-insensitivity for packages on Windows
3711
File JavaDoc packageDir = new File JavaDoc(grandParentDir,part);
3712            equal = packageDir.equals(parentDir);
3713          }
3714          else {
3715            // grand parent does not exist, so we can't create a File object
3716
// with the package's name. Just compare names; this doesn't
3717
// handle case-insensitivity for packages on Windows
3718
equal = part.equals(parentDir.getName());
3719          }
3720          if (!equal) {
3721            String JavaDoc msg = "The source file " + sourceFile.getAbsolutePath() +
3722              " is in the wrong directory or in the wrong package. " +
3723              "The directory name " + parentDir.getName() +
3724              " does not match the package component " + part + ".";
3725
3726            throw new InvalidPackageException(-1, msg);
3727          }
3728        }
3729
3730        // OK, now parentDir points to the directory of the first component of the
3731
// package name. The parent of that is the root.
3732
parentDir = parentDir.getParentFile();
3733        if (parentDir == null) {
3734          throw new RuntimeException JavaDoc("parent dir of first component is null?!");
3735        }
3736
3737        return parentDir;
3738      }
3739      catch (IOException JavaDoc ioe) {
3740        String JavaDoc msg = "Could not locate directory of the source file: " + ioe;
3741        throw new InvalidPackageException(-1, msg);
3742      }
3743    }
3744
3745    public String JavaDoc toString() { return getFileName(); }
3746    
3747    /** Orders ODDs by their id's. */
3748    public int compareTo(OpenDefinitionsDocument o) { return _id - o.id(); }
3749    
3750    /** Implementation of the javax.swing.text.Document interface. */
3751    public void addDocumentListener(DocumentListener JavaDoc listener) {
3752      if (_cacheAdapter.isReady()) getDocument().addDocumentListener(listener);
3753      else _cacheAdapter.getReconstructor().addDocumentListener(listener);
3754    }
3755    
3756    List JavaDoc<UndoableEditListener JavaDoc> _undoableEditListeners = new LinkedList JavaDoc<UndoableEditListener JavaDoc>();
3757    
3758    public void addUndoableEditListener(UndoableEditListener JavaDoc listener) {
3759      _undoableEditListeners.add(listener);
3760      getDocument().addUndoableEditListener(listener);
3761    }
3762    
3763    public void removeUndoableEditListener(UndoableEditListener JavaDoc listener) {
3764      _undoableEditListeners.remove(listener);
3765      getDocument().removeUndoableEditListener(listener);
3766    }
3767    
3768    public UndoableEditListener JavaDoc[] getUndoableEditListeners() {
3769      return getDocument().getUndoableEditListeners();
3770    }
3771    
3772    public Position JavaDoc createUnwrappedPosition(int offs) throws BadLocationException JavaDoc {
3773      return getDocument().createUnwrappedPosition(offs);
3774    }
3775
3776    public Position JavaDoc createPosition(int offs) throws BadLocationException JavaDoc {
3777      return getDocument().createPosition(offs);
3778    }
3779
3780    public Element JavaDoc getDefaultRootElement() { return getDocument().getDefaultRootElement(); }
3781    
3782    public Position JavaDoc getEndPosition() { return getDocument().getEndPosition(); }
3783    
3784    public int getLength() {
3785// synchronized(_cache._cacheLock) { // lock down the cache
3786
// if (_cacheAdapter.isReady() || _image == null)
3787
return getDocument().getLength();
3788// return _image.length();
3789
// }
3790
}
3791
3792    public Object JavaDoc getProperty(Object JavaDoc key) { return getDocument().getProperty(key); }
3793    
3794    public Element JavaDoc[] getRootElements() { return getDocument().getRootElements(); }
3795    
3796    public Position JavaDoc getStartPosition() { return getDocument().getStartPosition(); }
3797    
3798// public String getText() {
3799
// synchronized(_cache._cacheLock) { // lock down the cache
3800
// if (! _cacheAdapter.isReady() && _image != null) return _image;
3801
// }
3802
// return getDocumentText();
3803
// }
3804

3805// The following method must be renamed as private getDocumentText if the preceding code is commented in.
3806

3807    /** Gets the text of this. */
3808    public String JavaDoc getText() { return getDocument().getText(); }
3809    
3810    public String JavaDoc getText(int offset, int length) throws BadLocationException JavaDoc {
3811      return getDocument().getText(offset, length);
3812    }
3813    
3814    public void getText(int offset, int length, Segment JavaDoc txt) throws BadLocationException JavaDoc {
3815      getDocument().getText(offset, length, txt);
3816    }
3817    
3818    public void insertString(int offset, String JavaDoc str, AttributeSet JavaDoc a) throws BadLocationException JavaDoc {
3819      getDocument().insertString(offset, str, a);
3820    }
3821    
3822    public void append(String JavaDoc str, AttributeSet JavaDoc set) { getDocument().append(str, set); }
3823    
3824    public void append(String JavaDoc str, Style JavaDoc style) { getDocument().append(str, style); }
3825    
3826    public void putProperty(Object JavaDoc key, Object JavaDoc value) { getDocument().putProperty(key, value); }
3827    
3828    public void remove(int offs, int len) throws BadLocationException JavaDoc { getDocument().remove(offs, len); }
3829    
3830    public void removeDocumentListener(DocumentListener JavaDoc listener) {
3831      getDocument().removeDocumentListener(listener);
3832    }
3833    
3834    public void render(Runnable JavaDoc r) { getDocument().render(r); }
3835    
3836    /** End implementation of javax.swing.text.Document interface. */
3837    
3838    /** If the undo manager is unavailable, no undos are available
3839     * @return whether the undo manager can perform any undo's
3840     */

3841    public boolean undoManagerCanUndo() { return _cacheAdapter.isReady() && getUndoManager().canUndo(); }
3842    /**
3843     * If the undo manager is unavailable, no redos are available
3844     * @return whether the undo manager can perform any redo's
3845     */

3846    public boolean undoManagerCanRedo() { return _cacheAdapter.isReady() && getUndoManager().canRedo(); }
3847    
3848    /** Decorator pattern for the definitions document. */
3849    public CompoundUndoManager getUndoManager() { return getDocument().getUndoManager(); }
3850    
3851    public int getLineStartPos(int pos) { return getDocument().getLineStartPos(pos); }
3852    
3853    public int getLineEndPos(int pos) { return getDocument().getLineEndPos(pos); }
3854    
3855    public int commentLines(int selStart, int selEnd) { return getDocument().commentLines(selStart, selEnd); }
3856    
3857    public int uncommentLines(int selStart, int selEnd) {
3858      return getDocument().uncommentLines(selStart, selEnd);
3859    }
3860    
3861    public void indentLines(int selStart, int selEnd) { getDocument().indentLines(selStart, selEnd); }
3862    
3863    public int getCurrentLine() { return getDocument().getCurrentLine(); }
3864    
3865    public int getCurrentCol() { return getDocument().getCurrentCol(); }
3866    
3867    public boolean getClassFileInSync() { return getDocument().getClassFileInSync(); }
3868    
3869    public int getIntelligentBeginLinePos(int currPos) throws BadLocationException JavaDoc {
3870      return getDocument().getIntelligentBeginLinePos(currPos);
3871    }
3872    
3873    public int getOffset(int lineNum) { return getDocument().getOffset(lineNum); }
3874    
3875    public String JavaDoc getQualifiedClassName() throws ClassNameNotFoundException {
3876      return getDocument().getQualifiedClassName();
3877    }
3878    
3879    public String JavaDoc getQualifiedClassName(int pos) throws ClassNameNotFoundException {
3880      return getDocument().getQualifiedClassName(pos);
3881    }
3882    
3883    public ReducedModelState getStateAtCurrent() { return getDocument().getStateAtCurrent(); }
3884    
3885    public void resetUndoManager() {
3886      // if it's not in the cache, the undo manager will be reset when it's reconstructed
3887
if (_cacheAdapter.isReady()) getDocument().resetUndoManager();
3888    }
3889      
3890    public File JavaDoc getCachedClassFile() { return getDocument().getCachedClassFile(); }
3891      
3892    public void setCachedClassFile(File JavaDoc f) { getDocument().setCachedClassFile(f); }
3893    
3894    public DocumentListener JavaDoc[] getDocumentListeners() { return getDocument().getDocumentListeners(); }
3895    
3896    //--------- DJDocument methods ----------
3897

3898    public void setTab(String JavaDoc tab, int pos) { getDocument().setTab(tab,pos); }
3899    
3900    public int getWhiteSpace() { return getDocument().getWhiteSpace(); }
3901    
3902    public boolean posInParenPhrase(int pos) { return getDocument().posInParenPhrase(pos); }
3903    
3904    public boolean posInParenPhrase() { return getDocument().posInParenPhrase(); }
3905
3906    public String JavaDoc getEnclosingClassName(int pos, boolean fullyQualified) throws BadLocationException JavaDoc, ClassNameNotFoundException {
3907      return getDocument().getEnclosingClassName(pos, fullyQualified);
3908    }
3909    
3910    public int findPrevEnclosingBrace(int pos, char opening, char closing) throws BadLocationException JavaDoc {
3911      return getDocument().findPrevEnclosingBrace(pos, opening, closing);
3912    }
3913
3914    public int findNextEnclosingBrace(int pos, char opening, char closing) throws BadLocationException JavaDoc {
3915      return getDocument().findNextEnclosingBrace(pos, opening, closing);
3916    }
3917    
3918    public int findPrevNonWSCharPos(int pos) throws BadLocationException JavaDoc {
3919      return getDocument().findPrevNonWSCharPos(pos);
3920    }
3921    
3922    public int getFirstNonWSCharPos(int pos) throws BadLocationException JavaDoc {
3923      return getDocument().getFirstNonWSCharPos(pos);
3924    }
3925    
3926    public int getFirstNonWSCharPos(int pos, boolean acceptComments) throws BadLocationException JavaDoc {
3927      return getDocument().getFirstNonWSCharPos(pos, acceptComments);
3928    }
3929    
3930    public int getFirstNonWSCharPos (int pos, char[] whitespace, boolean acceptComments)
3931      throws BadLocationException JavaDoc {
3932      return getDocument().getFirstNonWSCharPos(pos, whitespace, acceptComments);
3933    }
3934    
3935    public int getLineFirstCharPos(int pos) throws BadLocationException JavaDoc {
3936      return getDocument().getLineFirstCharPos(pos);
3937    }
3938    
3939    public int findCharOnLine(int pos, char findChar) {
3940      return getDocument().findCharOnLine(pos, findChar);
3941    }
3942    
3943    public String JavaDoc getIndentOfCurrStmt(int pos) throws BadLocationException JavaDoc {
3944      return getDocument().getIndentOfCurrStmt(pos);
3945    }
3946    
3947    public String JavaDoc getIndentOfCurrStmt(int pos, char[] delims) throws BadLocationException JavaDoc {
3948      return getDocument().getIndentOfCurrStmt(pos, delims);
3949    }
3950    
3951    public String JavaDoc getIndentOfCurrStmt(int pos, char[] delims, char[] whitespace) throws BadLocationException JavaDoc {
3952      return getDocument().getIndentOfCurrStmt(pos, delims, whitespace);
3953    }
3954    
3955    public void indentLines(int selStart, int selEnd, int reason, ProgressMonitor JavaDoc pm)
3956      throws OperationCanceledException {
3957      getDocument().indentLines(selStart, selEnd, reason, pm);
3958    }
3959    
3960    public int findPrevCharPos(int pos, char[] whitespace) throws BadLocationException JavaDoc {
3961      return getDocument().findPrevCharPos(pos, whitespace);
3962    }
3963    
3964    public boolean findCharInStmtBeforePos(char findChar, int position) {
3965      return getDocument().findCharInStmtBeforePos(findChar, position);
3966    }
3967    
3968    public int findPrevDelimiter(int pos, char[] delims) throws BadLocationException JavaDoc {
3969      return getDocument().findPrevDelimiter(pos, delims);
3970    }
3971    
3972    public int findPrevDelimiter(int pos, char[] delims, boolean skipParenPhrases) throws BadLocationException JavaDoc {
3973      return getDocument().findPrevDelimiter(pos, delims, skipParenPhrases);
3974    }
3975    
3976    public void resetReducedModelLocation() { getDocument().resetReducedModelLocation(); }
3977    
3978    public ReducedModelState stateAtRelLocation(int dist) { return getDocument().stateAtRelLocation(dist); }
3979    
3980    public IndentInfo getIndentInformation() { return getDocument().getIndentInformation(); }
3981    
3982    public void move(int dist) { getDocument().move(dist); }
3983    
3984    public Vector JavaDoc<HighlightStatus> getHighlightStatus(int start, int end) {
3985      return getDocument().getHighlightStatus(start, end);
3986    }
3987    
3988    public void setIndent(int indent) { getDocument().setIndent(indent); }
3989    
3990    public int getIndent() { return getDocument().getIndent(); }
3991    
3992    //-----------------------
3993

3994    /** This method is put here because the ODD is the only way to get to the defdoc. */
3995    public void addFinalizationListener(FinalizationListener<DefinitionsDocument> fl) {
3996      getDocument().addFinalizationListener(fl);
3997    }
3998    
3999    public List JavaDoc<FinalizationListener<DefinitionsDocument>> getFinalizationListeners() {
4000      return getDocument().getFinalizationListeners();
4001    }
4002    
4003    // Styled Document Methods
4004
public Font JavaDoc getFont(AttributeSet JavaDoc attr) { return getDocument().getFont(attr); }
4005    
4006    public Color JavaDoc getBackground(AttributeSet JavaDoc attr) { return getDocument().getBackground(attr); }
4007    
4008    public Color JavaDoc getForeground(AttributeSet JavaDoc attr) { return getDocument().getForeground(attr); }
4009    
4010    public Element JavaDoc getCharacterElement(int pos) { return getDocument().getCharacterElement(pos); }
4011    
4012    public Element JavaDoc getParagraphElement(int pos) { return getDocument().getParagraphElement(pos); }
4013    
4014    public Style JavaDoc getLogicalStyle(int p) { return getDocument().getLogicalStyle(p); }
4015    
4016    public void setLogicalStyle(int pos, Style JavaDoc s) { getDocument().setLogicalStyle(pos, s); }
4017    
4018    public void setCharacterAttributes(int offset, int length, AttributeSet JavaDoc s, boolean replace) {
4019      getDocument().setCharacterAttributes(offset, length, s, replace);
4020    }
4021    
4022    public void setParagraphAttributes(int offset, int length, AttributeSet JavaDoc s, boolean replace) {
4023      getDocument().setParagraphAttributes(offset, length, s, replace);
4024    }
4025    
4026    public Style JavaDoc getStyle(String JavaDoc nm) { return getDocument().getStyle(nm); }
4027    
4028    public void removeStyle(String JavaDoc nm) { getDocument().removeStyle(nm); }
4029    
4030    public Style JavaDoc addStyle(String JavaDoc nm, Style JavaDoc parent) { return getDocument().addStyle(nm, parent); }
4031    
4032    public void clear() { getDocument().clear(); }
4033     
4034    /* Locking operations in DJDocument interface */
4035    
4036    /** Swing-style readLock(). */
4037    public void acquireReadLock() { getDocument().acquireReadLock(); }
4038    
4039    /** Swing-style readUlLock(). */
4040    public void releaseReadLock() { getDocument().releaseReadLock(); }
4041    
4042    /** Swing-style writeLock(). */
4043    public void acquireWriteLock() { getDocument().acquireWriteLock(); }
4044    
4045    /** Swing-style writeUnlock(). */
4046    public void releaseWriteLock() { getDocument().releaseWriteLock(); }
4047    
4048// public int getLockState() { return getDocument().getLockState(); }
4049

4050    /** @return the number of lines in this document. */
4051    public int getNumberOfLines() { return getLineOfOffset(getEndPosition().getOffset()-1); }
4052    
4053    /** Translates an offset into the components text to a line number.
4054     * @param offset the offset >= 0
4055     * @return the line number >= 0 */

4056    public int getLineOfOffset(int offset) {
4057      return getDefaultRootElement().getElementIndex(offset);
4058    }
4059  } /* End of ConcreteOpenDefDoc */
4060
4061  private static class TrivialFSS implements FileSaveSelector {
4062    private File JavaDoc _file;
4063    private TrivialFSS(File JavaDoc file) { _file = file; }
4064    public File JavaDoc getFile() throws OperationCanceledException { return _file; }
4065    public boolean warnFileOpen(File JavaDoc f) { return true; }
4066    public boolean verifyOverwrite() { return true; }
4067    public boolean shouldSaveAfterFileMoved(OpenDefinitionsDocument doc, File JavaDoc oldFile) { return true; }
4068  }
4069  
4070  /** Creates a ConcreteOpenDefDoc for a new DefinitionsDocument.
4071   * @return OpenDefinitionsDocument object for a new document
4072   */

4073  protected ConcreteOpenDefDoc _createOpenDefinitionsDocument() { return new ConcreteOpenDefDoc(); }
4074 
4075  /** Creates a ConcreteOpenDefDoc for a given file f
4076   * @return OpenDefinitionsDocument object for f
4077   */

4078  protected ConcreteOpenDefDoc _createOpenDefinitionsDocument(File JavaDoc f) throws IOException JavaDoc {
4079    if (! f.exists()) throw new FileNotFoundException JavaDoc("file " + f + " cannot be found");
4080    return new ConcreteOpenDefDoc(f);
4081  }
4082    
4083 /** Returns the OpenDefinitionsDocument corresponding to the given File, or null if that file is not open.
4084   * @param file File object to search for
4085   * @return Corresponding OpenDefinitionsDocument, or null
4086   */

4087  protected OpenDefinitionsDocument _getOpenDocument(File JavaDoc file) {
4088    synchronized(_documentsRepos) { return _documentsRepos.get(file); }
4089  }
4090 
4091  /** Returns the OpenDefinitionsDocuments that are NOT identified as project source files. */
4092  public List JavaDoc<OpenDefinitionsDocument> getNonProjectDocuments() {
4093    List JavaDoc<OpenDefinitionsDocument> allDocs = getOpenDefinitionsDocuments();
4094    List JavaDoc<OpenDefinitionsDocument> selectedDocs = new LinkedList JavaDoc<OpenDefinitionsDocument>();
4095    for (OpenDefinitionsDocument d : allDocs) {
4096      if (! d.inProjectPath() && ! d.isAuxiliaryFile ()) selectedDocs.add(d);
4097    }
4098    return selectedDocs;
4099  }
4100 
4101   /** Returns the OpenDefinitionsDocuments that are identified as auxiliary project source files. */
4102  public List JavaDoc<OpenDefinitionsDocument> getAuxiliaryDocuments() {
4103    List JavaDoc<OpenDefinitionsDocument> allDocs = getOpenDefinitionsDocuments();
4104    List JavaDoc<OpenDefinitionsDocument> selectedDocs = new LinkedList JavaDoc<OpenDefinitionsDocument>();
4105    for (OpenDefinitionsDocument d : allDocs)
4106      if (d.isAuxiliaryFile()) selectedDocs.add(d);
4107    return selectedDocs;
4108  }
4109 
4110  /** Returns the OpenDefinitionsDocuments that are identified as project source files. */
4111  public List JavaDoc<OpenDefinitionsDocument> getProjectDocuments() {
4112    List JavaDoc<OpenDefinitionsDocument> allDocs = getOpenDefinitionsDocuments();
4113    List JavaDoc<OpenDefinitionsDocument> projectDocs = new LinkedList JavaDoc<OpenDefinitionsDocument>();
4114    for (OpenDefinitionsDocument d: allDocs)
4115      if (d.inProjectPath() || d.isAuxiliaryFile()) projectDocs.add(d);
4116    return projectDocs;
4117  }
4118  /* Extracts relative path (from project origin) to parent of file identified by path. Assumes path does not end in
4119   * File.separator. TODO: convert this method to take a File argument. */

4120  public String JavaDoc fixPathForNavigator(String JavaDoc path) throws IOException JavaDoc {
4121    String JavaDoc parent = path.substring(0, path.lastIndexOf(File.separator ));
4122    String JavaDoc topLevelPath;
4123    String JavaDoc rootPath = getProjectRoot().getCanonicalPath();
4124    
4125    if (! parent.equals(rootPath) && ! parent.startsWith(rootPath + File.separator))
4126      /** it's an external file, so don't give it a path */
4127      return "";
4128    else
4129      return parent.substring(rootPath.length());
4130  }
4131 
4132  /** Creates an OpenDefinitionsDocument for a file. Does not add to the navigator or notify that the file's open.
4133   * This method should be called only from within another open method that will do all of this clean up.
4134   * @param file the file to open
4135   */

4136  private OpenDefinitionsDocument _rawOpenFile(File JavaDoc file) throws IOException JavaDoc, AlreadyOpenException{
4137    OpenDefinitionsDocument openDoc = _getOpenDocument(file);
4138    if (openDoc != null) throw new AlreadyOpenException(openDoc); // handled in MainFrame.openFile(...)
4139
final ConcreteOpenDefDoc doc = _createOpenDefinitionsDocument(file);
4140    if (file instanceof DocFile) {
4141      DocFile df = (DocFile)file;
4142      Pair<Integer JavaDoc,Integer JavaDoc> scroll = df.getScroll();
4143      Pair<Integer JavaDoc,Integer JavaDoc> sel = df.getSelection();
4144      String JavaDoc pkg = df.getPackage();
4145      doc.setPackage(pkg); // Trust information in the project file; if it is wrong, _packageName invariant is broken
4146
doc.setInitialVScroll(scroll.first());
4147      doc.setInitialHScroll( scroll.second());
4148      doc.setInitialSelStart(sel.first());
4149      doc.setInitialSelEnd(sel.second());
4150    }
4151    else {
4152// Utilities.show("Opened a file " + file.getName() + " that is not a DocFile");
4153
doc.setPackage(doc.getPackageNameFromDocument()); // get the package name from the file; forces file to be read
4154
}
4155    return doc;
4156  }
4157 
4158  /** This pop method enables an ArrayList to serve as stack. */
4159  protected static <T> T pop(ArrayList JavaDoc<T> stack) { return stack.remove(stack.size() - 1); }
4160 
4161  /** Creates an iNavigatorItem for a document, and adds it to the navigator. A helper for opening a file or creating
4162   * a new file.
4163   * @param doc the document to add to the navigator
4164   */

4165  protected void addDocToNavigator(final OpenDefinitionsDocument doc) {
4166    Utilities.invokeLater(new SRunnable() {
4167      public void run() {
4168        try {
4169          if (doc.isUntitled()) _documentNavigator.addDocument(doc);
4170          else {
4171            String JavaDoc path = doc.getFile().getCanonicalPath();
4172            _documentNavigator.addDocument(doc, fixPathForNavigator(path));
4173          }
4174        }
4175        catch(IOException JavaDoc e) { _documentNavigator.addDocument(doc); }
4176      }});
4177      synchronized(_documentsRepos) { _documentsRepos.put(doc.getRawFile(), doc); }
4178  }
4179 
4180  /** Add a document to the classpath for the slave JVM. Does nothing here because there is no slave JVM. Overridden
4181    * in DefaultGlobalModel. */

4182  protected void addDocToClassPath(OpenDefinitionsDocument doc) { }
4183 
4184  /** Creates a document from a file.
4185   * @param file File to read document from
4186   * @return openened document
4187   */

4188  public OpenDefinitionsDocument _openFile(File JavaDoc file) throws IOException JavaDoc, AlreadyOpenException {
4189    
4190    OpenDefinitionsDocument doc = _rawOpenFile(file);
4191    _completeOpenFile(doc);
4192    return doc;
4193  }
4194 
4195  private void _completeOpenFile(OpenDefinitionsDocument d) {
4196      addDocToNavigator(d);
4197      addDocToClassPath(d);
4198      
4199      try {
4200        File JavaDoc f = d.getFile();
4201        if (!inProject(f) && inProjectPath(d)) {
4202          setProjectChanged(true);
4203        }
4204      } catch(FileMovedException fme) {
4205        /** project is not modified in this case */
4206      }
4207      
4208      _notifier.fileOpened(d);
4209  }
4210 
4211  private static class BackUpFileOptionListener implements OptionListener<Boolean JavaDoc> {
4212    public void optionChanged (OptionEvent<Boolean JavaDoc> oe) {
4213      Boolean JavaDoc value = oe.value;
4214      FileOps.DefaultFileSaver.setBackupsEnabled (value.booleanValue());
4215    }
4216  }
4217
4218//----------------------- SingleDisplay Methods -----------------------//
4219

4220  /** Returns the currently active document. */
4221  public OpenDefinitionsDocument getActiveDocument() { return _activeDocument; }
4222 
4223  /** Sets the currently active document by updating the selection model.
4224   * @param doc Document to set as active
4225   */

4226  public void setActiveDocument(final OpenDefinitionsDocument doc) {
4227    /* The following code fixes a potential race because this method modifies the documentNavigator which is a swing
4228     * component. Hence it must run in the event thread. Note that setting the active document triggers the execution
4229     * of listeners some of which also need to run in the event thread.
4230     *
4231     * The _activeDoc field is set by _gainVisitor when the DocumentNavigator changes the active document.
4232     */

4233    
4234// if (_activeDocument == doc) return; // this optimization appears to cause some subtle bugs
4235
// Utilities.showDebug("DEBUG: Called setActiveDocument()");
4236

4237    try {
4238      Utilities.invokeAndWait(new SRunnable() {
4239        public void run() {
4240// doc.makePositions(); // reconstruct the embedded postions in this document (reconstructs document if necesarry)
4241
_documentNavigator.setNextChangeModelInitiated(true);
4242          addToBrowserHistory();
4243          _documentNavigator.setActiveDoc(doc);
4244        }
4245      });
4246    }
4247    catch(Exception JavaDoc e) { throw new UnexpectedException(e); }
4248  }
4249 
4250  public Container JavaDoc getDocCollectionWidget() { return _documentNavigator.asContainer(); }
4251 
4252  /** Sets the active document to be the next one in the collection. */
4253  public void setActiveNextDocument() {
4254    OpenDefinitionsDocument key = _activeDocument;
4255    OpenDefinitionsDocument nextKey = _documentNavigator.getNext(key);
4256    if (key != nextKey) setActiveDocument(nextKey);
4257    else setActiveDocument(_documentNavigator.getFirst());
4258    /* selects the active document in the navigator, which signals a listener to call _setActiveDoc(...) */
4259  }
4260
4261  /** Sets the active document to be the previous one in the collection. */
4262  public void setActivePreviousDocument() {
4263    OpenDefinitionsDocument key = _activeDocument;
4264    OpenDefinitionsDocument prevKey = _documentNavigator.getPrevious(key);
4265    if (key != prevKey) setActiveDocument(prevKey);
4266    else setActiveDocument(_documentNavigator.getLast());
4267      /* selects the active document in the navigator, which signals a listener to call _setActiveDoc(...) */
4268  }
4269
4270  //----------------------- End SingleDisplay Methods -----------------------//
4271

4272  /** Returns whether there is currently only one open document which is untitled and unchanged. */
4273  private boolean _hasOneEmptyDocument() {
4274    return getOpenDefinitionsDocumentsSize() == 1 && _activeDocument.isUntitled() &&
4275            ! _activeDocument.isModifiedSinceSave();
4276  }
4277
4278  /** Creates a new document if there are currently no documents open. */
4279  private void _ensureNotEmpty() {
4280    if (getOpenDefinitionsDocumentsSize() == 0) newFile(getMasterWorkingDirectory());
4281  }
4282 
4283  /** Makes sure that none of the documents in the list are active.
4284   * Should only be executed in event thread.
4285   */

4286  private void _ensureNotActive(List JavaDoc<OpenDefinitionsDocument> docs) {
4287    if (docs.contains(getActiveDocument())) {
4288      // Find the one that should be the new active document
4289
IDocumentNavigator<OpenDefinitionsDocument> nav = getDocumentNavigator();
4290      
4291      OpenDefinitionsDocument item = docs.get(docs.size()-1);
4292      OpenDefinitionsDocument nextActive = nav.getNext(item);
4293      if (!nextActive.equals(item)) {
4294        setActiveDocument(nextActive);
4295        return;
4296      }
4297      
4298      item = docs.get(0);
4299      nextActive = nav.getPrevious(item);
4300      if (!nextActive.equals(item)) {
4301        setActiveDocument(nextActive);
4302        return;
4303      }
4304      
4305      throw new RuntimeException JavaDoc("No document to set active before closing");
4306    }
4307  }
4308 
4309  /** Sets the first document in the navigator as active. */
4310  public void setActiveFirstDocument() {
4311 
4312    /* Selects the active document in the navigator, which signals a listener to call _setActiveDoc(...). */
4313    setActiveDocument(getOpenDefinitionsDocuments().get(0));
4314  }
4315 
4316  private void _setActiveDoc(INavigatorItem idoc) {
4317// synchronized (this) {
4318
_activeDocument = (OpenDefinitionsDocument) idoc;
4319// }
4320
refreshActiveDocument();
4321  }
4322 
4323  /** Invokes the activeDocumentChanged method in the global listener on the argument _activeDocument. This process
4324   * sets up _activeDocument as the document in the definitions pane. It is also necessary after an "All Documents"
4325   * search that wraps around. */

4326  public void refreshActiveDocument() {
4327    try {
4328      _activeDocument.checkIfClassFileInSync();
4329      // notify single display model listeners // notify single display model listeners
4330
_notifier.activeDocumentChanged(_activeDocument);
4331    } catch(DocumentClosedException dce) { /* do nothing */ }
4332  }
4333}
4334
4335
Popular Tags