0001: /*BEGIN_COPYRIGHT_BLOCK
0002: *
0003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
0004: * All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions are met:
0008: * * Redistributions of source code must retain the above copyright
0009: * notice, this list of conditions and the following disclaimer.
0010: * * Redistributions in binary form must reproduce the above copyright
0011: * notice, this list of conditions and the following disclaimer in the
0012: * documentation and/or other materials provided with the distribution.
0013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
0014: * names of its contributors may be used to endorse or promote products
0015: * derived from this software without specific prior written permission.
0016: *
0017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0028: *
0029: * This software is Open Source Initiative approved Open Source Software.
0030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
0031: *
0032: * This file is part of DrJava. Download the current version of this project
0033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
0034: *
0035: * END_COPYRIGHT_BLOCK*/
0036: package edu.rice.cs.drjava.model;
0037:
0038: import java.awt.Color;
0039: import java.awt.Container;
0040: import java.awt.EventQueue;
0041: import java.awt.Font;
0042: import java.awt.event.FocusEvent;
0043: import java.awt.event.FocusListener;
0044: import java.awt.print.PageFormat;
0045: import java.awt.print.Pageable;
0046: import java.awt.print.PrinterException;
0047: import java.awt.print.PrinterJob;
0048: import java.io.ByteArrayInputStream;
0049: import java.io.File;
0050: import java.io.FileFilter;
0051: import java.io.FileNotFoundException;
0052: import java.io.FileReader;
0053: import java.io.FilenameFilter;
0054: import java.io.InputStreamReader;
0055: import java.io.IOException;
0056: import java.io.OutputStream;
0057: import java.io.StringReader;
0058:
0059: import java.util.AbstractMap;
0060: import java.util.ArrayList;
0061: import java.util.Collections;
0062: import java.util.Comparator;
0063: import java.util.Set;
0064: import java.util.LinkedHashSet;
0065: import java.util.HashSet;
0066: import java.util.Hashtable;
0067: import java.util.LinkedHashMap;
0068: import java.util.LinkedList;
0069: import java.util.List;
0070: import java.util.ListIterator;
0071: import java.util.Vector;
0072: import java.util.WeakHashMap;
0073:
0074: import javax.swing.SwingUtilities;
0075: import javax.swing.event.DocumentListener;
0076: import javax.swing.event.UndoableEditListener;
0077: import javax.swing.text.AttributeSet;
0078: import javax.swing.text.BadLocationException;
0079: import javax.swing.text.DefaultEditorKit;
0080: import javax.swing.text.Element;
0081: import javax.swing.text.Position;
0082: import javax.swing.text.Segment;
0083: import javax.swing.text.Style;
0084: import javax.swing.ProgressMonitor;
0085:
0086: import edu.rice.cs.drjava.DrJava;
0087: import edu.rice.cs.drjava.DrJavaRoot;
0088: import edu.rice.cs.drjava.config.FileOption;
0089: import edu.rice.cs.drjava.config.OptionConstants;
0090: import edu.rice.cs.drjava.config.OptionEvent;
0091: import edu.rice.cs.drjava.config.OptionListener;
0092: import edu.rice.cs.drjava.model.cache.DCacheAdapter;
0093: import edu.rice.cs.drjava.model.cache.DDReconstructor;
0094: import edu.rice.cs.drjava.model.cache.DocumentCache;
0095: import edu.rice.cs.drjava.model.compiler.CompilerModel;
0096: import edu.rice.cs.drjava.model.debug.Breakpoint;
0097: import edu.rice.cs.drjava.model.debug.DebugBreakpointData;
0098: import edu.rice.cs.drjava.model.debug.DebugException;
0099: import edu.rice.cs.drjava.model.debug.DebugWatchData;
0100: import edu.rice.cs.drjava.model.debug.Debugger;
0101: import edu.rice.cs.drjava.model.debug.NoDebuggerAvailable;
0102: import edu.rice.cs.drjava.model.javadoc.JavadocModel;
0103: import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException;
0104: import edu.rice.cs.drjava.model.definitions.CompoundUndoManager;
0105: import edu.rice.cs.drjava.model.definitions.DefinitionsDocument; //import edu.rice.cs.drjava.model.definitions.DefinitionsDocument.WrappedPosition;
0106: import edu.rice.cs.drjava.model.definitions.DefinitionsEditorKit;
0107: import edu.rice.cs.drjava.model.definitions.DocumentUIListener;
0108: import edu.rice.cs.drjava.model.definitions.InvalidPackageException;
0109: import edu.rice.cs.drjava.model.definitions.indent.Indenter;
0110: import edu.rice.cs.drjava.model.definitions.reducedmodel.HighlightStatus;
0111: import edu.rice.cs.drjava.model.definitions.reducedmodel.IndentInfo;
0112: import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelState;
0113: import edu.rice.cs.drjava.model.junit.JUnitModel;
0114: import edu.rice.cs.drjava.model.print.DrJavaBook;
0115: import edu.rice.cs.drjava.model.repl.DefaultInteractionsModel;
0116: import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
0117: import edu.rice.cs.drjava.model.repl.InteractionsDocument;
0118: import edu.rice.cs.drjava.model.repl.InteractionsScriptModel;
0119: import edu.rice.cs.drjava.project.DocFile;
0120: import edu.rice.cs.drjava.project.DocumentInfoGetter;
0121: import edu.rice.cs.drjava.project.MalformedProjectFileException;
0122: import edu.rice.cs.drjava.project.ProjectFileIR;
0123: import edu.rice.cs.drjava.project.ProjectFileParser;
0124: import edu.rice.cs.drjava.project.ProjectProfile;
0125: import edu.rice.cs.drjava.ui.MainFrame;
0126: import edu.rice.cs.drjava.ui.SplashScreen;
0127:
0128: import edu.rice.cs.plt.tuple.Pair;
0129: import edu.rice.cs.plt.io.IOUtil;
0130: import edu.rice.cs.plt.iter.IterUtil;
0131: import edu.rice.cs.plt.lambda.LambdaUtil;
0132: import edu.rice.cs.plt.lambda.Predicate;
0133:
0134: import edu.rice.cs.util.FileOpenSelector;
0135: import edu.rice.cs.util.FileOps;
0136: import edu.rice.cs.util.Lambda;
0137: import edu.rice.cs.util.Log;
0138: import edu.rice.cs.util.NullFile;
0139: import edu.rice.cs.util.OperationCanceledException;
0140: import edu.rice.cs.util.OrderedHashSet;
0141: import edu.rice.cs.util.SRunnable;
0142: import edu.rice.cs.util.StringOps;
0143: import edu.rice.cs.util.UnexpectedException;
0144: import edu.rice.cs.util.docnavigation.AWTContainerNavigatorFactory;
0145: import edu.rice.cs.util.docnavigation.IDocumentNavigator;
0146: import edu.rice.cs.util.docnavigation.INavigationListener;
0147: import edu.rice.cs.util.docnavigation.INavigatorItem;
0148: import edu.rice.cs.util.docnavigation.INavigatorItemFilter;
0149: import edu.rice.cs.util.docnavigation.JTreeSortNavigator;
0150: import edu.rice.cs.util.docnavigation.NodeData;
0151: import edu.rice.cs.util.docnavigation.NodeDataVisitor;
0152: import edu.rice.cs.util.swing.AsyncCompletionArgs;
0153: import edu.rice.cs.util.swing.AsyncTask;
0154: import edu.rice.cs.util.swing.IAsyncProgress;
0155: import edu.rice.cs.util.swing.DocumentIterator;
0156: import edu.rice.cs.util.swing.Utilities;
0157: import edu.rice.cs.util.text.AbstractDocumentInterface;
0158: import edu.rice.cs.util.text.ConsoleDocument;
0159: import edu.rice.cs.util.ReaderWriterLock;
0160:
0161: import static java.lang.Math.*;
0162:
0163: /** In simple terms, a DefaultGlobalModel without an interpreter, compiler, junit testing, debugger or javadoc.
0164: * Hence, it only has only document handling functionality
0165: * @version $Id: AbstractGlobalModel.java 4257 2007-09-12 00:29:25Z mgricken $
0166: */
0167: public class AbstractGlobalModel implements SingleDisplayModel,
0168: OptionConstants, DocumentIterator {
0169:
0170: public static Log _log = new Log("GlobalModel.txt", false);
0171:
0172: /** A document cache that manages how many unmodified documents are open at once. */
0173: protected DocumentCache _cache;
0174:
0175: static final String DOCUMENT_OUT_OF_SYNC_MSG = "Current document is out of sync with the Interactions Pane and should be recompiled!\n";
0176:
0177: static final String CLASSPATH_OUT_OF_SYNC_MSG = "Interactions Pane is out of sync with the current classpath and should be reset!\n";
0178:
0179: // ----- FIELDS -----
0180:
0181: /** Adds a document to the list of auxiliary files. The LinkedList class is not thread safe, so
0182: * the add operation is synchronized.
0183: */
0184: public void addAuxiliaryFile(OpenDefinitionsDocument doc) {
0185: _state.addAuxFile(doc.getRawFile());
0186: }
0187:
0188: /** Removes a document from the list of auxiliary files. The LinkedList class is not thread safe, so
0189: * operations on _auxiliaryFiles are synchronized.
0190: */
0191: public void removeAuxiliaryFile(OpenDefinitionsDocument doc) {
0192: _state.remAuxFile(doc.getRawFile());
0193: }
0194:
0195: /** Keeps track of all listeners to the model, and has the ability to notify them of some event. Originally used
0196: * a Command Pattern style, but this has been replaced by having EventNotifier directly implement all listener
0197: * interfaces it supports. Set in constructor so that subclasses can install their own notifier with additional
0198: * methods.
0199: */
0200: public final GlobalEventNotifier _notifier = new GlobalEventNotifier();
0201:
0202: // ---- Definitions fields ----
0203:
0204: /** Factory for new definitions documents and views.*/
0205: protected final DefinitionsEditorKit _editorKit = new DefinitionsEditorKit(
0206: _notifier);
0207:
0208: /** Collection for storing all OpenDefinitionsDocuments. */
0209: private final AbstractMap<File, OpenDefinitionsDocument> _documentsRepos = new LinkedHashMap<File, OpenDefinitionsDocument>();
0210:
0211: // ---- Input/Output Document Fields ----
0212:
0213: /** The document used to display System.out and System.err, and to read from System.in. */
0214: protected final ConsoleDocument _consoleDoc;
0215:
0216: /** The document adapter used in the console document. */
0217: protected final InteractionsDJDocument _consoleDocAdapter;
0218:
0219: /** Number of milliseconds to wait after each println, to prevent the JVM from being flooded with print calls.
0220: * TODO: why is this here, and why is it public?
0221: */
0222: public static final int WRITE_DELAY = 5;
0223:
0224: /** A PageFormat object for printing to paper. */
0225: protected volatile PageFormat _pageFormat = new PageFormat();
0226:
0227: /** The active document pointer, which will never be null once the constructor is done.
0228: * Maintained by the _gainVisitor with a navigation listener.
0229: */
0230: private volatile OpenDefinitionsDocument _activeDocument;
0231:
0232: /** A pointer to the active directory, which is not necessarily the parent of the active document
0233: * The user may click on a folder component in the navigation pane and that will set this field without
0234: * setting the active document. It is used by the newFile method to place new files into the active directory.
0235: */
0236: private volatile File _activeDirectory;
0237:
0238: /** A state varible indicating whether the class path has changed. Reset to false by resetInteractions. */
0239: private volatile boolean classPathChanged = false;
0240:
0241: /** The abstract container which contains views of open documents and allows user to navigate document focus among
0242: * this collection of open documents
0243: */
0244: protected volatile IDocumentNavigator<OpenDefinitionsDocument> _documentNavigator = new AWTContainerNavigatorFactory<OpenDefinitionsDocument>()
0245: .makeListNavigator();
0246:
0247: /** Manager for breakpoint regions. */
0248: protected final ConcreteRegionManager<Breakpoint> _breakpointManager;
0249:
0250: /** @return manager for breakpoint regions. */
0251: public RegionManager<Breakpoint> getBreakpointManager() {
0252: return _breakpointManager;
0253: }
0254:
0255: /** Manager for bookmark regions. */
0256: protected final ConcreteRegionManager<DocumentRegion> _bookmarkManager;
0257:
0258: /** @return manager for bookmark regions. */
0259: public RegionManager<DocumentRegion> getBookmarkManager() {
0260: return _bookmarkManager;
0261: }
0262:
0263: /** Managers for find result regions. */
0264: protected final LinkedList<RegionManager<MovingDocumentRegion>> _findResultsManagers;
0265:
0266: /** @return manager for find result regions. */
0267: public List<RegionManager<MovingDocumentRegion>> getFindResultsManagers() {
0268: return new LinkedList<RegionManager<MovingDocumentRegion>>(
0269: _findResultsManagers);
0270: }
0271:
0272: /** @return new manager for find result regions. */
0273: public RegionManager<MovingDocumentRegion> createFindResultsManager() {
0274: ConcreteRegionManager<MovingDocumentRegion> rm = new ConcreteRegionManager<MovingDocumentRegion>();
0275: _findResultsManagers.add(rm);
0276:
0277: // install new manager in all documents
0278: for (final OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
0279: doc.addFindResultsManager(rm);
0280: }
0281: return rm;
0282: }
0283:
0284: /** Dispose a manager for find result regions. */
0285: public void disposeFindResultsManager(
0286: RegionManager<MovingDocumentRegion> rm) {
0287: // remove manager from all documents
0288: for (final OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
0289: doc.removeFindResultsManager(rm);
0290: }
0291: _findResultsManagers.remove(rm);
0292: }
0293:
0294: /** Manager for browser history regions. */
0295: protected final ConcreteRegionManager<DocumentRegion> _browserHistoryManager;
0296:
0297: /** @return manager for browser history regions. */
0298: public RegionManager<DocumentRegion> getBrowserHistoryManager() {
0299: return _browserHistoryManager;
0300: }
0301:
0302: // Any lightweight parsing has been disabled until we have something that is beneficial and works better in the background.
0303: // /** Light-weight parsing controller. */
0304: // protected LightWeightParsingControl _parsingControl;
0305: //
0306: // /** @return the parsing control */
0307: // public LightWeightParsingControl getParsingControl() { return _parsingControl; }
0308:
0309: // ----- CONSTRUCTORS -----
0310:
0311: /** Constructs a new GlobalModel. Creates a new MainJVM and starts its Interpreter JVM. */
0312: public AbstractGlobalModel() {
0313: _cache = new DocumentCache();
0314:
0315: _consoleDocAdapter = new InteractionsDJDocument();
0316: _consoleDoc = new ConsoleDocument(_consoleDocAdapter);
0317:
0318: _bookmarkManager = new ConcreteRegionManager<DocumentRegion>();
0319: _findResultsManagers = new LinkedList<RegionManager<MovingDocumentRegion>>();
0320: _browserHistoryManager = new ConcreteRegionManager<DocumentRegion>();
0321:
0322: _breakpointManager = new ConcreteRegionManager<Breakpoint>() {
0323: public boolean changeRegionHelper(final Breakpoint oldBP,
0324: final Breakpoint newBP) {
0325: // override helper so the enabled flag is copied
0326: if (oldBP.isEnabled() != newBP.isEnabled()) {
0327: oldBP.setEnabled(newBP.isEnabled());
0328: return true;
0329: }
0330: return false;
0331: }
0332: };
0333:
0334: _registerOptionListeners();
0335:
0336: setFileGroupingState(makeFlatFileGroupingState());
0337: _notifier.projectRunnableChanged();
0338: _init();
0339: }
0340:
0341: private void _init() {
0342:
0343: /** This visitor is invoked by the DocumentNavigator to update _activeDocument among other things */
0344: final NodeDataVisitor<OpenDefinitionsDocument, Boolean> _gainVisitor = new NodeDataVisitor<OpenDefinitionsDocument, Boolean>() {
0345: public Boolean itemCase(OpenDefinitionsDocument doc,
0346: Object... p) {
0347: Boolean modelInitiated = (Boolean) p[0];
0348: // if (!modelInitiated) {
0349: // IDocumentNavigator.LOG.log("_gainVisitor; modelInitiated = false");
0350: // }
0351: OpenDefinitionsDocument oldDoc = AbstractGlobalModel.this
0352: .getActiveDocument();
0353: // if (! modelInitiated) { addToBrowserHistory(); }
0354: _setActiveDoc(doc); // sets _activeDocument, the shadow copy of the active document
0355: // if (! modelInitiated) { addToBrowserHistory(); }
0356:
0357: // Utilities.showDebug("Setting the active doc done");
0358: final File oldDir = _activeDirectory; // _activeDirectory can be null
0359: final File dir = doc.getParentDirectory(); // dir can be null
0360: if (dir != null && !dir.equals(oldDir)) {
0361: /* If the file is in External or Auxiliary Files then then we do not want to change our project directory
0362: * to something outside the project. ?? */
0363: _activeDirectory = dir;
0364: _notifier.currentDirectoryChanged(_activeDirectory);
0365: }
0366: return Boolean.valueOf(true);
0367: }
0368:
0369: public Boolean fileCase(File f, Object... p) {
0370: if (!f.isAbsolute()) { // should never happen because all file names are canonicalized
0371: File root = _state.getProjectFile().getParentFile()
0372: .getAbsoluteFile();
0373: f = new File(root, f.getPath());
0374: }
0375: _activeDirectory = f; // Invariant: activeDirectory != null
0376: _notifier.currentDirectoryChanged(f);
0377: return Boolean.valueOf(true);
0378: }
0379:
0380: public Boolean stringCase(String s, Object... p) {
0381: return Boolean.valueOf(false);
0382: }
0383: };
0384:
0385: /** Listener that invokes the _gainVisitor when a selection is made in the document navigator. */
0386: _documentNavigator
0387: .addNavigationListener(new INavigationListener<OpenDefinitionsDocument>() {
0388: public void gainedSelection(
0389: NodeData<? extends OpenDefinitionsDocument> dat,
0390: boolean modelInitiated) {
0391: dat.execute(_gainVisitor, modelInitiated);
0392: }
0393:
0394: public void lostSelection(
0395: NodeData<? extends OpenDefinitionsDocument> dat,
0396: boolean modelInitiated) {
0397: /* not important, only one document selected at a time */}
0398: });
0399:
0400: // The document navigator gets the focus in
0401: _documentNavigator.addFocusListener(new FocusListener() {
0402: public void focusGained(FocusEvent e) {
0403: // System.err.println("_documentNavigator.focusGained(...) called");
0404: // if (_documentNavigator.getCurrent() != null) // past selection is leaf node
0405: _notifier.focusOnDefinitionsPane();
0406: }
0407:
0408: public void focusLost(FocusEvent e) {
0409: }
0410: });
0411:
0412: _ensureNotEmpty();
0413: setActiveFirstDocument();
0414:
0415: // setup option listener for clipboard history
0416: OptionListener<Integer> clipboardHistorySizeListener = new OptionListener<Integer>() {
0417: public void optionChanged(OptionEvent<Integer> oce) {
0418: ClipboardHistoryModel.singleton().resize(oce.value);
0419: }
0420: };
0421: DrJava.getConfig().addOptionListener(CLIPBOARD_HISTORY_SIZE,
0422: clipboardHistorySizeListener);
0423: ClipboardHistoryModel.singleton().resize(
0424: DrJava.getConfig().getSetting(CLIPBOARD_HISTORY_SIZE)
0425: .intValue());
0426:
0427: // setup option listener for browser history
0428: OptionListener<Integer> browserHistoryMaxSizeListener = new OptionListener<Integer>() {
0429: public void optionChanged(OptionEvent<Integer> oce) {
0430: AbstractGlobalModel.this .getBrowserHistoryManager()
0431: .setMaximumSize(oce.value);
0432: }
0433: };
0434: DrJava.getConfig().addOptionListener(BROWSER_HISTORY_MAX_SIZE,
0435: browserHistoryMaxSizeListener);
0436: getBrowserHistoryManager().setMaximumSize(
0437: DrJava.getConfig().getSetting(BROWSER_HISTORY_MAX_SIZE)
0438: .intValue());
0439: }
0440:
0441: // ----- STATE -----
0442:
0443: /** Specifies the state of the navigator pane. The global model delegates the compileAll command to the _state, a
0444: * FileGroupingState.nSynchronization is handled by the compilerModel.
0445: */
0446: protected volatile FileGroupingState _state;
0447:
0448: /** @param state the new file grouping state. */
0449: public void setFileGroupingState(FileGroupingState state) {
0450: _state = state;
0451: _notifier.projectRunnableChanged();
0452: _notifier.projectBuildDirChanged();
0453: _notifier.projectWorkDirChanged();
0454: // _notifier.projectModified(); // not currently used
0455: }
0456:
0457: protected FileGroupingState makeProjectFileGroupingState(File pr,
0458: File main, File bd, File wd, File project, File[] srcFiles,
0459: File[] auxFiles, Iterable<File> cp, File cjf, int cjflags) {
0460: return new ProjectFileGroupingState(pr, main, bd, wd, project,
0461: srcFiles, auxFiles, cp, cjf, cjflags);
0462: }
0463:
0464: /** @return true if the class path state has been changed. */
0465: public boolean isClassPathChanged() {
0466: return classPathChanged;
0467: }
0468:
0469: /** Updates the classpath state. */
0470: public void setClassPathChanged(boolean changed) {
0471: classPathChanged = changed;
0472: }
0473:
0474: /** Notifies the project state that the project has been changed. */
0475: public void setProjectChanged(boolean changed) {
0476: _state.setProjectChanged(changed);
0477: // _notifier.projectModified(); // not currently used
0478: }
0479:
0480: /** @return true if the project state has been changed. */
0481: public boolean isProjectChanged() {
0482: return _state.isProjectChanged();
0483: }
0484:
0485: /** @return true if the model has a project open, false otherwise. */
0486: public boolean isProjectActive() {
0487: return _state.isProjectActive();
0488: }
0489:
0490: /** @return the file that points to the current project file. Null if not currently in project view
0491: */
0492: public File getProjectFile() {
0493: return _state.getProjectFile();
0494: }
0495:
0496: /** @return all files currently saved as source files in the project file.
0497: * If _state not in project mode, returns null
0498: */
0499: public File[] getProjectFiles() {
0500: return _state.getProjectFiles();
0501: }
0502:
0503: /** @return true the given file is in the current project file. */
0504: public boolean inProject(File f) {
0505: return _state.inProject(f);
0506: }
0507:
0508: /** A file is in the project if the source root is the same as the
0509: * project root. this means that project files must be saved at the
0510: * source root. (we query the model through the model's state)
0511: */
0512: public boolean inProjectPath(OpenDefinitionsDocument doc) {
0513: return _state.inProjectPath(doc);
0514: }
0515:
0516: /** Sets the class with the project's main method. */
0517: public void setMainClass(File f) {
0518: _state.setMainClass(f);
0519: _notifier.projectRunnableChanged();
0520: setProjectChanged(true);
0521: }
0522:
0523: /** @return the class with the project's main method. */
0524: public File getMainClass() {
0525: return _state.getMainClass();
0526: }
0527:
0528: /** Sets the create jar file of the project. */
0529: public void setCreateJarFile(File f) {
0530: _state.setCreateJarFile(f);
0531: setProjectChanged(true);
0532: }
0533:
0534: /** Return the create jar file for the project. If not in project mode, returns null. */
0535: public File getCreateJarFile() {
0536: return _state.getCreateJarFile();
0537: }
0538:
0539: /** Sets the create jar flags of the project. */
0540: public void setCreateJarFlags(int f) {
0541: _state.setCreateJarFlags(f);
0542: setProjectChanged(true);
0543: }
0544:
0545: /** Return the create jar flags for the project. If not in project mode, returns 0. */
0546: public int getCreateJarFlags() {
0547: return _state.getCreateJarFlags();
0548: }
0549:
0550: /** @return the root of the project sourc tree (assuming one exists). */
0551: public File getProjectRoot() {
0552: return _state.getProjectRoot();
0553: }
0554:
0555: /** Sets the class with the project's main method. Degenerate version overridden in DefaultGlobalModel. */
0556: public void setProjectRoot(File f) {
0557: _state.setProjectRoot(f);
0558: // _notifier.projectRootChanged();
0559: setProjectChanged(true);
0560: }
0561:
0562: /** Sets project file to specifed value; used in "Save Project As ..." command in MainFrame. */
0563: public void setProjectFile(File f) {
0564: _state.setProjectFile(f);
0565: }
0566:
0567: /** @return the build directory for the project (assuming one exists). */
0568: public File getBuildDirectory() {
0569: return _state.getBuildDirectory();
0570: }
0571:
0572: /** Sets the class with the project's main method. Degenerate version overridden in DefaultGlobalModel. */
0573: public void setBuildDirectory(File f) {
0574: _state.setBuildDirectory(f);
0575: _notifier.projectBuildDirChanged();
0576: setProjectChanged(true);
0577: }
0578:
0579: /** @return the working directory for the Master JVM (editor and GUI). */
0580: public File getMasterWorkingDirectory() {
0581: File file;
0582: try {
0583: // restore the path from the configuration
0584: file = FileOps.getValidDirectory(DrJava.getConfig()
0585: .getSetting(LAST_DIRECTORY));
0586: } catch (RuntimeException e) {
0587: // something went wrong, clear the setting and use "user.home"
0588: DrJava.getConfig().setSetting(LAST_DIRECTORY,
0589: FileOption.NULL_FILE);
0590: file = FileOps.getValidDirectory(new File(System
0591: .getProperty("user.home", ".")));
0592: }
0593: // update the setting and return it
0594: DrJava.getConfig().setSetting(LAST_DIRECTORY, file);
0595: return file;
0596: }
0597:
0598: /** @return the working directory for the Slave (Interactions) JVM */
0599: public File getWorkingDirectory() {
0600: return _state.getWorkingDirectory();
0601: }
0602:
0603: /** Sets the working directory for the project; ignored in flat file model. */
0604: public void setWorkingDirectory(File f) {
0605: _state.setWorkingDirectory(f);
0606: _notifier.projectWorkDirChanged();
0607: setProjectChanged(true);
0608: // update the setting
0609: DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY,
0610: _state.getWorkingDirectory());
0611: }
0612:
0613: public void cleanBuildDirectory() {
0614: _state.cleanBuildDirectory();
0615: }
0616:
0617: public List<File> getClassFiles() {
0618: return _state.getClassFiles();
0619: }
0620:
0621: /** Helper method used in subsequent anonymous inner class */
0622: protected static String getPackageName(String classname) {
0623: int index = classname.lastIndexOf(".");
0624: if (index != -1)
0625: return classname.substring(0, index);
0626: else
0627: return "";
0628: }
0629:
0630: class ProjectFileGroupingState implements FileGroupingState {
0631:
0632: volatile File _projRoot;
0633: volatile File _mainFile;
0634: volatile File _buildDir;
0635: volatile File _workDir;
0636: volatile File _projectFile;
0637: final File[] _projectFiles;
0638: volatile Vector<File> _auxFiles;
0639: volatile Iterable<File> _projExtraClassPath;
0640: private boolean _isProjectChanged = false;
0641: volatile File _createJarFile;
0642: volatile int _createJarFlags;
0643:
0644: HashSet<String> _projFilePaths = new HashSet<String>();
0645:
0646: /** Degenerate constructor for a new project; only the file project name is known. */
0647: ProjectFileGroupingState(File project) {
0648: this (project.getParentFile(), null, null, null, project,
0649: new File[0], new File[0], IterUtil.<File> empty(),
0650: null, 0);
0651: }
0652:
0653: ProjectFileGroupingState(File pr, File main, File bd, File wd,
0654: File project, File[] srcFiles, File[] auxFiles,
0655: Iterable<File> cp, File cjf, int cjflags) {
0656: _projRoot = pr;
0657: _mainFile = main;
0658: _buildDir = bd;
0659: _workDir = wd;
0660: _projectFile = project;
0661: _projectFiles = srcFiles;
0662: _auxFiles = new Vector<File>(auxFiles.length);
0663: for (File f : auxFiles) {
0664: _auxFiles.add(f);
0665: }
0666: _projExtraClassPath = cp;
0667:
0668: if (_projectFiles != null)
0669: try {
0670: for (File file : _projectFiles) {
0671: _projFilePaths.add(file.getCanonicalPath());
0672: }
0673: } catch (IOException e) { /*do nothing */
0674: }
0675:
0676: _createJarFile = cjf;
0677: _createJarFlags = cjflags;
0678: }
0679:
0680: public boolean isProjectActive() {
0681: return true;
0682: }
0683:
0684: /** Determines whether the specified doc in within the project file tree.
0685: * No synchronization is required because only immutable data is accessed.
0686: */
0687: public boolean inProjectPath(OpenDefinitionsDocument doc) {
0688: if (doc.isUntitled())
0689: return false;
0690:
0691: /* If the file does not exist, we still want to tell if it's path lies within the project source tree. The file
0692: * may have existed previously at one point and then removed, in which case we should treat it as an untitled
0693: * project file that should be resaved. */
0694: File f;
0695: try {
0696: f = doc.getFile();
0697: } catch (FileMovedException fme) {
0698: f = fme.getFile();
0699: }
0700: return inProjectPath(f);
0701: }
0702:
0703: /** Determines whether the specified file in within the project file tree. No synchronization is required because
0704: * only immutable data is accessed.
0705: */
0706: public boolean inProjectPath(File f) {
0707: return IOUtil.isMember(f, getProjectRoot());
0708: }
0709:
0710: /** @return the absolute path to the project file. Since projectFile is final, no synchronization is necessary.*/
0711: public File getProjectFile() {
0712: return _projectFile;
0713: }
0714:
0715: public boolean inProject(File f) {
0716: String path;
0717:
0718: if (isUntitled(f) || !inProjectPath(f))
0719: return false;
0720: try {
0721: path = f.getCanonicalPath();
0722: return _projFilePaths.contains(path);
0723: } catch (IOException ioe) {
0724: return false;
0725: }
0726: }
0727:
0728: public File[] getProjectFiles() {
0729: return _projectFiles;
0730: }
0731:
0732: public File getProjectRoot() {
0733: if (_projRoot == null
0734: || _projRoot.equals(FileOption.NULL_FILE))
0735: return _projectFile.getParentFile();
0736: // Utilities.show("File grouping state returning project root of " + _projRoot);
0737: return _projRoot;
0738: }
0739:
0740: public File getBuildDirectory() {
0741: return _buildDir;
0742: }
0743:
0744: public File getWorkingDirectory() {
0745: try {
0746: if (_workDir == null
0747: || _workDir == FileOption.NULL_FILE) {
0748: File parentDir = _projectFile.getParentFile();
0749: if (parentDir != null) {
0750: return parentDir.getCanonicalFile(); // default is project root
0751: } else
0752: return new File(System.getProperty("user.dir"));
0753: }
0754: return _workDir.getCanonicalFile();
0755: } catch (IOException e) { /* fall through */
0756: }
0757: return _workDir.getAbsoluteFile();
0758: }
0759:
0760: /** Sets project file to specifed value; used in "Save Project As ..." command in MainFrame. */
0761: public void setProjectFile(File f) {
0762: _projectFile = f;
0763: }
0764:
0765: public void setProjectRoot(File f) {
0766: _projRoot = f;
0767: // System.err.println("Project root set to " + f);
0768: }
0769:
0770: /** Adds File f to end of _auxFiles vector. */
0771: public void addAuxFile(File f) {
0772: synchronized (_auxFiles) {
0773: if (_auxFiles.add(f)) {
0774: setProjectChanged(true);
0775: }
0776: setProjectChanged(true);
0777: }
0778: }
0779:
0780: /** Removes File f from _auxFiles list. */
0781: public void remAuxFile(File file) {
0782: synchronized (_auxFiles) {
0783: if (_auxFiles.remove(file)) {
0784: setProjectChanged(true);
0785: }
0786: setProjectChanged(true);
0787: }
0788: }
0789:
0790: public void setBuildDirectory(File f) {
0791: _buildDir = f;
0792: }
0793:
0794: public void setWorkingDirectory(File f) {
0795: _workDir = f;
0796: }
0797:
0798: public File getMainClass() {
0799: return _mainFile;
0800: }
0801:
0802: public void setMainClass(File f) {
0803: _mainFile = f;
0804: }
0805:
0806: public void setCreateJarFile(File f) {
0807: _createJarFile = f;
0808: }
0809:
0810: public File getCreateJarFile() {
0811: return _createJarFile;
0812: }
0813:
0814: public void setCreateJarFlags(int f) {
0815: _createJarFlags = f;
0816: }
0817:
0818: public int getCreateJarFlags() {
0819: return _createJarFlags;
0820: }
0821:
0822: public boolean isProjectChanged() {
0823: return _isProjectChanged;
0824: }
0825:
0826: public void setProjectChanged(boolean changed) {
0827: _isProjectChanged = changed;
0828: }
0829:
0830: public boolean isAuxiliaryFile(File f) {
0831: String path;
0832:
0833: if (isUntitled(f))
0834: return false;
0835:
0836: try {
0837: path = f.getCanonicalPath();
0838: } catch (IOException ioe) {
0839: return false;
0840: }
0841:
0842: synchronized (_auxFiles) {
0843: for (File file : _auxFiles) {
0844: try {
0845: if (file.getCanonicalPath().equals(path))
0846: return true;
0847: } catch (IOException ioe) { /* ignore file */
0848: }
0849: }
0850: return false;
0851: }
0852: }
0853:
0854: // This only starts the process. It is all done asynchronously.
0855: public void cleanBuildDirectory() {
0856: File dir = this .getBuildDirectory();
0857: _notifier.executeAsyncTask(_findFilesToCleanTask, dir,
0858: false, true);
0859: }
0860:
0861: private AsyncTask<File, List<File>> _findFilesToCleanTask = new AsyncTask<File, List<File>>(
0862: "Find Files to Clean") {
0863: private FilenameFilter _filter = new FilenameFilter() {
0864: public boolean accept(File parent, String name) {
0865: return new File(parent, name).isDirectory()
0866: || name.endsWith(".class");
0867: }
0868: };
0869:
0870: public List<File> runAsync(File buildDir,
0871: IAsyncProgress monitor) throws Exception {
0872: List<File> accumulator = new LinkedList<File>();
0873: helper(buildDir, accumulator); // adds files to the accumulator recursively
0874: return accumulator;
0875: }
0876:
0877: public void complete(AsyncCompletionArgs<List<File>> args) {
0878: _notifier.executeAsyncTask(_deleteFilesTask, args
0879: .getResult(), true, true);
0880: }
0881:
0882: public String getDiscriptionMessage() {
0883: return "Finding files to delete...";
0884: }
0885:
0886: private void helper(File file, List<File> accumulator) {
0887: if (file.isDirectory()) {
0888: File[] children = file.listFiles(_filter);
0889: for (File child : children) {
0890: helper(child, accumulator);
0891: accumulator.add(file);
0892: }
0893: } else if (file.getName().endsWith(".class")) {
0894: accumulator.add(file);
0895: }
0896: }
0897: };
0898:
0899: private AsyncTask<List<File>, List<File>> _deleteFilesTask = new AsyncTask<List<File>, List<File>>(
0900: "Delete Files") {
0901: public List<File> runAsync(List<File> filesToDelete,
0902: IAsyncProgress monitor) throws Exception {
0903: List<File> undeletableFiles = new ArrayList<File>();
0904:
0905: monitor.setMinimum(0);
0906: monitor.setMaximum(filesToDelete.size());
0907: int progress = 1;
0908: for (File file : filesToDelete) {
0909: if (monitor.isCanceled()) {
0910: break;
0911: }
0912: monitor.setNote(file.getName());
0913: boolean could = file.delete();
0914: if (!could)
0915: undeletableFiles.add(file);
0916: monitor.setProgress(progress++);
0917: }
0918: // if (! dir.exists()) dir.mkdirs (); // TODO: figure out where to put this.
0919: return undeletableFiles;
0920: }
0921:
0922: public void complete(AsyncCompletionArgs<List<File>> args) {
0923: // TODO: user feedback. Maybe add a method to the notifier to set the status bar text
0924: }
0925:
0926: public String getDiscriptionMessage() {
0927: return "Deleting files...";
0928: }
0929: };
0930:
0931: public List<File> getClassFiles() {
0932: File dir = this .getBuildDirectory();
0933: LinkedList<File> acc = new LinkedList<File>();
0934: getClassFilesHelper(dir, acc);
0935: if (!dir.exists())
0936: dir.mkdirs();
0937: return acc;
0938: }
0939:
0940: private void getClassFilesHelper(File f, LinkedList<File> acc) {
0941: if (f.isDirectory()) {
0942:
0943: File fs[] = f.listFiles(new FilenameFilter() {
0944: public boolean accept(File parent, String name) {
0945: return new File(parent, name).isDirectory()
0946: || name.endsWith(".class");
0947: }
0948: });
0949:
0950: if (fs != null) { // listFiles may return null if there's an IO error
0951: for (File kid : fs) {
0952: getClassFilesHelper(kid, acc);
0953: }
0954: }
0955:
0956: } else if (f.getName().endsWith(".class"))
0957: acc.add(f);
0958: }
0959:
0960: // ----- FIND ALL DEFINED CLASSES IN FOLDER ---
0961:
0962: public Iterable<File> getExtraClassPath() {
0963: return _projExtraClassPath;
0964: }
0965:
0966: public void setExtraClassPath(Iterable<File> cp) {
0967: _projExtraClassPath = cp;
0968: setClassPathChanged(true);
0969: }
0970: }
0971:
0972: protected FileGroupingState makeFlatFileGroupingState() {
0973: return new FlatFileGroupingState();
0974: }
0975:
0976: class FlatFileGroupingState implements FileGroupingState {
0977: public File getBuildDirectory() {
0978: return null;
0979: }
0980:
0981: public File getProjectRoot() {
0982: return getWorkingDirectory();
0983: }
0984:
0985: public File getWorkingDirectory() {
0986: Iterable<File> roots = getSourceRootSet();
0987: if (!IterUtil.isEmpty(roots)) {
0988: return IterUtil.first(roots);
0989: } else {
0990: // use the last directory saved to the configuration
0991: File file = null;
0992: if (DrJava.getConfig().getSetting(
0993: STICKY_INTERACTIONS_DIRECTORY)) {
0994: try {
0995: // restore the path from the configuration
0996: file = FileOps.getValidDirectory(DrJava
0997: .getConfig().getSetting(
0998: LAST_INTERACTIONS_DIRECTORY));
0999: } catch (RuntimeException e) {
1000: file = null;
1001: }
1002: }
1003: if (file == null) {
1004: // something went wrong, clear the setting and use "user.home"
1005: file = FileOps.getValidDirectory(new File(System
1006: .getProperty("user.home", ".")));
1007: }
1008: // update the setting and return it
1009: DrJava.getConfig().setSetting(
1010: LAST_INTERACTIONS_DIRECTORY, file);
1011: return file;
1012: }
1013: }
1014:
1015: public boolean isProjectActive() {
1016: return false;
1017: }
1018:
1019: public boolean inProjectPath(OpenDefinitionsDocument doc) {
1020: return false;
1021: }
1022:
1023: public boolean inProjectPath(File f) {
1024: return false;
1025: }
1026:
1027: public File getProjectFile() {
1028: return null;
1029: }
1030:
1031: public void setBuildDirectory(File f) {
1032: }
1033:
1034: public void setProjectFile(File f) {
1035: }
1036:
1037: public void setProjectRoot(File f) {
1038: }
1039:
1040: public void addAuxFile(File f) {
1041: }
1042:
1043: public void remAuxFile(File f) {
1044: }
1045:
1046: public void setWorkingDirectory(File f) {
1047: }
1048:
1049: public File[] getProjectFiles() {
1050: return null;
1051: }
1052:
1053: public boolean inProject(File f) {
1054: return false;
1055: }
1056:
1057: public File getMainClass() {
1058: return null;
1059: }
1060:
1061: public void setMainClass(File f) {
1062: }
1063:
1064: public void setCreateJarFile(File f) {
1065: }
1066:
1067: public File getCreateJarFile() {
1068: return null;
1069: }
1070:
1071: public void setCreateJarFlags(int f) {
1072: }
1073:
1074: public int getCreateJarFlags() {
1075: return 0;
1076: }
1077:
1078: public Iterable<File> getExtraClassPath() {
1079: return IterUtil.empty();
1080: }
1081:
1082: public void setExtraClassPath(Iterable<File> cp) {
1083: }
1084:
1085: public boolean isProjectChanged() {
1086: return false;
1087: }
1088:
1089: public void setProjectChanged(boolean changed) { /* Do nothing */
1090: }
1091:
1092: public boolean isAuxiliaryFile(File f) {
1093: return false;
1094: }
1095:
1096: public void cleanBuildDirectory() {
1097: }
1098:
1099: public List<File> getClassFiles() {
1100: return new LinkedList<File>();
1101: }
1102: }
1103:
1104: /** Gives the title of the source bin for the navigator.
1105: * @return The text used for the source bin in the tree navigator
1106: */
1107: public String getSourceBinTitle() {
1108: return "[ Source Files ]";
1109: }
1110:
1111: /** Gives the title of the external files bin for the navigator
1112: * @return The text used for the external files bin in the tree navigator.
1113: */
1114: public String getExternalBinTitle() {
1115: return "[ External Files ]";
1116: }
1117:
1118: /** Gives the title of the aux files bin for the navigator.
1119: * @return The text used for the aux files bin in the tree navigator.
1120: */
1121: public String getAuxiliaryBinTitle() {
1122: return "[ Included External Files ]";
1123: }
1124:
1125: // ----- METHODS -----
1126:
1127: /** Add a listener to this global model.
1128: * @param listener a listener that reacts on events generated by the GlobalModel.
1129: */
1130: public void addListener(GlobalModelListener listener) {
1131: _notifier.addListener(listener);
1132: }
1133:
1134: /** Remove a listener from this global model.
1135: * @param listener a listener that reacts on events generated by the GlobalModel
1136: * This method is synchronized using the readers/writers event protocol incorporated in EventNotifier<T>.
1137: */
1138: public void removeListener(GlobalModelListener listener) {
1139: _notifier.removeListener(listener);
1140: }
1141:
1142: // getter methods for the private fields
1143:
1144: public DefinitionsEditorKit getEditorKit() {
1145: return _editorKit;
1146: }
1147:
1148: /** throws UnsupportedOperationException */
1149: public DefaultInteractionsModel getInteractionsModel() {
1150: throw new UnsupportedOperationException(
1151: "AbstractGlobalModel does not support interaction");
1152: }
1153:
1154: /** throws UnsupportedOperationException */
1155: public InteractionsDJDocument getSwingInteractionsDocument() {
1156: throw new UnsupportedOperationException(
1157: "AbstractGlobalModel does not support interaction");
1158: }
1159:
1160: /** throws UnsupportedOperationException */
1161: public InteractionsDocument getInteractionsDocument() {
1162: throw new UnsupportedOperationException(
1163: "AbstractGlobalModel does not support interaction");
1164: }
1165:
1166: public ConsoleDocument getConsoleDocument() {
1167: return _consoleDoc;
1168: }
1169:
1170: public InteractionsDJDocument getSwingConsoleDocument() {
1171: return _consoleDocAdapter;
1172: }
1173:
1174: public PageFormat getPageFormat() {
1175: return _pageFormat;
1176: }
1177:
1178: public void setPageFormat(PageFormat format) {
1179: _pageFormat = format;
1180: }
1181:
1182: public CompilerModel getCompilerModel() {
1183: throw new UnsupportedOperationException(
1184: "AbstractGlobalModel does not support compilation");
1185: }
1186:
1187: /** throws UnsupportedOperationException */
1188: public int getNumCompErrors() {
1189: throw new UnsupportedOperationException(
1190: "AbstractGlobalModel does not support compilation");
1191: }
1192:
1193: /** throws UnsupportedOperationException */
1194: public void setNumCompErrors(int num) {
1195: throw new UnsupportedOperationException(
1196: "AbstractGlobalModel does not support compilation");
1197: };
1198:
1199: /** throws UnsupportedOperationException */
1200: public JUnitModel getJUnitModel() {
1201: throw new UnsupportedOperationException(
1202: "AbstractGlobalModel does not support unit testing");
1203: }
1204:
1205: /** throws UnsupportedOperationException */
1206: public JavadocModel getJavadocModel() {
1207: throw new UnsupportedOperationException(
1208: "AbstractGlobalModel does not support javadoc");
1209: }
1210:
1211: public IDocumentNavigator<OpenDefinitionsDocument> getDocumentNavigator() {
1212: return _documentNavigator;
1213: }
1214:
1215: public void setDocumentNavigator(
1216: IDocumentNavigator<OpenDefinitionsDocument> newnav) {
1217: _documentNavigator = newnav;
1218: }
1219:
1220: /** Creates a new open definitions document and adds it to the list. Public for testing purposes.
1221: * @return The new open document
1222: */
1223: public OpenDefinitionsDocument newFile(File parentDir) {
1224: final ConcreteOpenDefDoc doc = _createOpenDefinitionsDocument(new NullFile());
1225: doc.setParentDirectory(parentDir);
1226: addDocToNavigator(doc);
1227: _notifier.newFileCreated(doc);
1228: return doc;
1229: }
1230:
1231: /** Creates a new document, adds it to the list of open documents, and sets it to be active.
1232: * @return The new open document
1233: */
1234: public OpenDefinitionsDocument newFile() {
1235: File dir = _activeDirectory;
1236: if (dir == null)
1237: dir = getMasterWorkingDirectory();
1238: // addToBrowserHistory();
1239: OpenDefinitionsDocument doc = newFile(dir);
1240: setActiveDocument(doc);
1241: return doc;
1242: }
1243:
1244: /** Creates a new junit test case.
1245: * @param name the name of the new test case
1246: * @param makeSetUp true iff an empty setUp() method should be included
1247: * @param makeTearDown true iff an empty tearDown() method should be included
1248: * @return the new open test case
1249: */
1250: public OpenDefinitionsDocument newTestCase(String name,
1251: boolean makeSetUp, boolean makeTearDown) {
1252: boolean elementary = (DrJava.getConfig().getSetting(
1253: LANGUAGE_LEVEL) == 1);
1254:
1255: final StringBuilder buf = new StringBuilder();
1256: if (!elementary)
1257: buf.append("import junit.framework.TestCase;\n\n");
1258: buf.append("/**\n");
1259: buf.append("* A JUnit test case class.\n");
1260: buf
1261: .append("* Every method starting with the word \"test\" will be called when running\n");
1262: buf.append("* the test with JUnit.\n");
1263: buf.append("*/\n");
1264: if (!elementary)
1265: buf.append("public ");
1266: buf.append("class ");
1267: buf.append(name);
1268: buf.append(" extends TestCase {\n\n");
1269: if (makeSetUp) {
1270: buf.append("/**\n");
1271: buf
1272: .append("* This method is called before each test method, to perform any common\n");
1273: buf.append("* setup if necessary.\n");
1274: buf.append("*/\n");
1275: if (!elementary)
1276: buf.append("public ");
1277: buf.append("void setUp() throws Exception {\n}\n\n");
1278: }
1279: if (makeTearDown) {
1280: buf.append("/**\n");
1281: buf
1282: .append("* This method is called after each test method, to perform any common\n");
1283: buf.append("* clean-up if necessary.\n");
1284: buf.append("*/\n");
1285: if (!elementary)
1286: buf.append("public ");
1287: buf.append("void tearDown() throws Exception {\n}\n\n");
1288: }
1289: buf.append("/**\n");
1290: buf.append("* A test method.\n");
1291: buf
1292: .append("* (Replace \"X\" with a name describing the test. You may write as\n");
1293: buf
1294: .append("* many \"testSomething\" methods in this class as you wish, and each\n");
1295: buf
1296: .append("* one will be called when running JUnit over this class.)\n");
1297: buf.append("*/\n");
1298: if (!elementary)
1299: buf.append("public ");
1300: buf.append("void testX() {\n}\n\n");
1301: buf.append("}\n");
1302: String test = buf.toString();
1303:
1304: OpenDefinitionsDocument openDoc = newFile();
1305: try {
1306: openDoc.insertString(0, test, null);
1307: openDoc.indentLines(0, test.length());
1308: } catch (BadLocationException ble) {
1309: throw new UnexpectedException(ble);
1310: }
1311: return openDoc;
1312: }
1313:
1314: /** This method is for use only by test cases. */
1315: public DocumentCache getDocumentCache() {
1316: return _cache;
1317: }
1318:
1319: //---------------------- Specified by ILoadDocuments ----------------------//
1320:
1321: /** Open a file and add it to the pool of definitions documents. The provided file selector chooses a file,
1322: * and on a successful open, the fileOpened() event is fired. This method also checks if there was previously
1323: * a single unchanged, untitled document open, and if so, closes it after a successful opening.
1324: * @param com a command pattern command that selects what file to open
1325: * @return The open document, or null if unsuccessful
1326: * @exception IOException
1327: * @exception OperationCanceledException if the open was canceled
1328: * @exception AlreadyOpenException if the file is already open
1329: */
1330: public OpenDefinitionsDocument openFile(FileOpenSelector com)
1331: throws IOException, OperationCanceledException,
1332: AlreadyOpenException {
1333: // Close an untitled, unchanged document if it is the only one open
1334: boolean closeUntitled = _hasOneEmptyDocument();
1335: OpenDefinitionsDocument oldDoc = _activeDocument;
1336: OpenDefinitionsDocument openedDoc = openFileHelper(com);
1337: if (closeUntitled)
1338: closeFileHelper(oldDoc);
1339: // Utilities.showDebug("DrJava has opened" + openedDoc + " and is setting it active");
1340: // addToBrowserHistory();
1341: setActiveDocument(openedDoc);
1342: setProjectChanged(true);
1343: // Utilities.showDebug("active doc set; openFile returning");
1344: return openedDoc;
1345: }
1346:
1347: protected OpenDefinitionsDocument openFileHelper(
1348: FileOpenSelector com) throws IOException,
1349: OperationCanceledException, AlreadyOpenException {
1350:
1351: // This code is duplicated in MainFrame._setCurrentDirectory(File) for safety.
1352: final File file = (com.getFiles())[0].getCanonicalFile(); // may throw an IOException if path is invalid
1353: OpenDefinitionsDocument odd = _openFile(file);
1354: // Utilities.showDebug("File " + file + " opened");
1355: // Make sure this is on the classpath
1356: try {
1357: File classPath = odd.getSourceRoot();
1358: addDocToClassPath(odd);
1359: setClassPathChanged(true);
1360: } catch (InvalidPackageException e) {
1361: // Invalid package-- don't add it to classpath
1362: }
1363:
1364: return odd;
1365: }
1366:
1367: /** Open multiple files and add them to the pool of definitions documents. The provided file selector chooses
1368: * a collection of files, and on successfully opening each file, the fileOpened() event is fired. This method
1369: * also checks if there was previously a single unchanged, untitled document open, and if so, closes it after
1370: * a successful opening.
1371: * @param com a command pattern command that selects what file to open
1372: * @return The open document, or null if unsuccessful
1373: * @exception IOException
1374: * @exception OperationCanceledException if the open was canceled
1375: * @exception AlreadyOpenException if the file is already open
1376: */
1377: public OpenDefinitionsDocument[] openFiles(FileOpenSelector com)
1378: throws IOException, OperationCanceledException,
1379: AlreadyOpenException {
1380:
1381: // Close an untitled, unchanged document if it is the only one open
1382: boolean closeUntitled = _hasOneEmptyDocument();
1383: OpenDefinitionsDocument oldDoc = _activeDocument;
1384:
1385: OpenDefinitionsDocument[] openedDocs = openFilesHelper(com);
1386: if (openedDocs.length > 0) {
1387: if (closeUntitled)
1388: closeFileHelper(oldDoc);
1389: // addToBrowserHistory();
1390: setActiveDocument(openedDocs[0]);
1391: }
1392: return openedDocs;
1393: }
1394:
1395: protected OpenDefinitionsDocument[] openFilesHelper(
1396: FileOpenSelector com) throws IOException,
1397: OperationCanceledException, AlreadyOpenException {
1398:
1399: final File[] files = com.getFiles();
1400: if (files == null) {
1401: throw new IOException("No Files returned from FileSelector");
1402: }
1403: OpenDefinitionsDocument[] docs = _openFiles(files);
1404: return docs;
1405: }
1406:
1407: // if set to true, and uncommented, the definitions document will
1408: // print out a small stack trace every time getDocument() is called
1409:
1410: // static boolean SHOW_GETDOC = false;
1411:
1412: /** Opens all the files in the list, and notifies about the last file opened. */
1413: private OpenDefinitionsDocument[] _openFiles(File[] files)
1414: throws IOException, OperationCanceledException,
1415: AlreadyOpenException {
1416:
1417: ArrayList<OpenDefinitionsDocument> alreadyOpenDocuments = new ArrayList<OpenDefinitionsDocument>();
1418: ArrayList<OpenDefinitionsDocument> retDocs = new ArrayList<OpenDefinitionsDocument>();
1419:
1420: // SHOW_GETDOC = true;
1421:
1422: LinkedList<File> filesNotFound = new LinkedList<File>();
1423: LinkedList<OpenDefinitionsDocument> filesOpened = new LinkedList<OpenDefinitionsDocument>();
1424: for (final File f : files) {
1425: if (f == null)
1426: throw new IOException(
1427: "File name returned from FileSelector is null");
1428: try {
1429: OpenDefinitionsDocument d = _rawOpenFile(IOUtil
1430: .attemptCanonicalFile(f));
1431: //always return last opened Doc
1432: retDocs.add(d);
1433: filesOpened.add(d);
1434: } catch (AlreadyOpenException aoe) {
1435: OpenDefinitionsDocument d = aoe.getOpenDocument();
1436: retDocs.add(d);
1437: alreadyOpenDocuments.add(d);
1438: } catch (FileNotFoundException e) {
1439: filesNotFound.add(f);
1440: }
1441: }
1442:
1443: for (final OpenDefinitionsDocument d : filesOpened) {
1444: _completeOpenFile(d); // contains view-related calls
1445: }
1446: // SHOW_GETDOC = false;
1447: if (filesNotFound.size() > 0)
1448: _notifier.filesNotFound(filesNotFound
1449: .toArray(new File[filesNotFound.size()]));
1450:
1451: if (!alreadyOpenDocuments.isEmpty()) {
1452: for (OpenDefinitionsDocument d : alreadyOpenDocuments) {
1453: _notifier.handleAlreadyOpenDocument(d);
1454: _notifier.fileOpened(d);
1455: }
1456: }
1457:
1458: if (retDocs != null) {
1459: return retDocs.toArray(new OpenDefinitionsDocument[0]);
1460: } else {
1461: //if we didn't open any files, then it's just as if they cancelled it...
1462: throw new OperationCanceledException();
1463: }
1464: }
1465:
1466: //----------------------- End ILoadDocuments Methods -----------------------//
1467:
1468: /** Opens all files in the specified folder dir and places them in the appropriate places in the document navigator.
1469: * If "open folders recursively" is checked, this operation opens all files in the subtree rooted at dir.
1470: */
1471: public void openFolder(File dir, boolean rec) throws IOException,
1472: OperationCanceledException, AlreadyOpenException {
1473: if (dir == null)
1474: return; // just in case
1475:
1476: if (dir.isDirectory()) {
1477: Iterable<File> filesIterable;
1478:
1479: String extension = DrJavaRoot.LANGUAGE_LEVEL_EXTENSIONS[DrJava
1480: .getConfig().getSetting(LANGUAGE_LEVEL)];
1481:
1482: Predicate<File> match = LambdaUtil.and(IOUtil.IS_FILE,
1483: IOUtil.extensionFilePredicate(extension));
1484: if (rec) {
1485: filesIterable = IOUtil.listFilesRecursively(dir, match);
1486: } else {
1487: filesIterable = IOUtil.attemptListFilesAsIterable(dir,
1488: match);
1489: }
1490: List<File> files = IterUtil.asList(filesIterable);
1491:
1492: if (isProjectActive()) {
1493: Collections.sort(files, new Comparator<File>() {
1494: public int compare(File o1, File o2) {
1495: return -o1.getAbsolutePath().compareTo(
1496: o2.getAbsolutePath());
1497: }
1498: });
1499: } else {
1500: Collections.sort(files, new Comparator<File>() {
1501: public int compare(File o1, File o2) {
1502: return -o1.getName().compareTo(o2.getName());
1503: }
1504: });
1505: }
1506:
1507: int ct = files.size();
1508:
1509: final File[] sfiles = files.toArray(new File[ct]);
1510:
1511: openFiles(new FileOpenSelector() {
1512: public File[] getFiles() {
1513: return sfiles;
1514: }
1515: });
1516:
1517: if (ct > 0 && _state.inProjectPath(dir))
1518: setProjectChanged(true);
1519: }
1520: }
1521:
1522: /** Saves all open files, prompting for names if necessary.
1523: * When prompting (i.e., untitled document), set that document as active.
1524: * @param com a FileSaveSelector
1525: * @exception IOException
1526: */
1527: public void saveAllFiles(FileSaveSelector com) throws IOException {
1528: OpenDefinitionsDocument curdoc = getActiveDocument();
1529: saveAllFilesHelper(com);
1530: setActiveDocument(curdoc); // Return focus to previously active doc
1531: }
1532:
1533: /** Called by saveAllFiles in DefaultGlobalModel */
1534: protected void saveAllFilesHelper(FileSaveSelector com)
1535: throws IOException {
1536:
1537: boolean isProjActive = isProjectActive();
1538:
1539: for (final OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) { // getOpen... makes a copy
1540: // do not force Untitled document to be saved if projectActive() or unmodified
1541: if (doc.isUntitled()
1542: && (isProjActive || !doc.isModifiedSinceSave()))
1543: continue;
1544: aboutToSaveFromSaveAll(doc);
1545: doc.saveFile(com);
1546: }
1547: }
1548:
1549: /** Creates a new FileGroupingState for specificed project file and default values for other properties.
1550: * @param projFile the new project file (which does not yet exist in the file system)
1551: */
1552: public void createNewProject(File projFile) {
1553: setFileGroupingState(new ProjectFileGroupingState(projFile));
1554: }
1555:
1556: /** Configures a new project (created by createNewProject) and writes it to disk; only runs in event thread. */
1557: public void configNewProject() throws IOException {
1558:
1559: assert EventQueue.isDispatchThread();
1560:
1561: // FileGroupingState oldState = _state;
1562: File projFile = getProjectFile();
1563:
1564: ProjectProfile builder = new ProjectProfile(projFile);
1565:
1566: // FileLists for project file
1567: ArrayList<DocFile> srcFileList = new ArrayList<DocFile>();
1568: LinkedList<DocFile> auxFileList = new LinkedList<DocFile>();
1569: ArrayList<File> extFileList = new ArrayList<File>();
1570:
1571: File projectRoot = builder.getProjectRoot();
1572:
1573: // Utilities.show("Fetched project root is " + projectRoot);
1574:
1575: List<File> exCp = new LinkedList<File>();
1576:
1577: for (OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
1578:
1579: File f = doc.getFile();
1580:
1581: if (doc.isUntitled())
1582: extFileList.add(f);
1583: else if (IOUtil.isMember(f, projectRoot)) {
1584: DocFile file = new DocFile(f);
1585: file.setPackage(doc.getPackageName()); // must save _packageName so it is correct when project is loaded
1586: builder.addSourceFile(file);
1587: srcFileList.add(file);
1588: } else if (doc.isAuxiliaryFile()) {
1589: DocFile file = new DocFile(f);
1590: file.setPackage(doc.getPackageName()); // must save _packageName so it is correct when project is loaded
1591: builder.addAuxiliaryFile(new DocFile(f));
1592: auxFileList.add(file);
1593: } else
1594: /* doc is external file */extFileList.add(f);
1595: }
1596:
1597: // DocFile[] srcFiles = srcFileList.toArray(new DocFile[srcFileList.size()]);
1598: // DocFile[] extFiles = extFileList.toArray(new DocFile[extFileList.size()]);
1599:
1600: // write to disk
1601: builder.write();
1602:
1603: _loadProject(builder);
1604: }
1605:
1606: /** Writes the project profile augmented by usage info to specified file. Assumes DrJava is in project mode.
1607: * @param file where to save the project
1608: * @param info
1609: */
1610: public ProjectProfile _makeProjectProfile(File file,
1611: Hashtable<OpenDefinitionsDocument, DocumentInfoGetter> info)
1612: throws IOException {
1613: ProjectProfile builder = new ProjectProfile(file);
1614:
1615: // add project root
1616: File pr = getProjectRoot();
1617: if (pr != null)
1618: builder.setProjectRoot(pr);
1619:
1620: // add opendefinitionsdocument
1621: ArrayList<File> srcFileList = new ArrayList<File>();
1622: LinkedList<File> auxFileList = new LinkedList<File>();
1623:
1624: for (OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
1625: if (doc.inProjectPath()) {
1626: DocumentInfoGetter g = info.get(doc);
1627: builder.addSourceFile(g);
1628: srcFileList.add(g.getFile());
1629: } else if (doc.isAuxiliaryFile()) {
1630: DocumentInfoGetter g = info.get(doc);
1631: builder.addAuxiliaryFile(g);
1632: auxFileList.add(g.getFile());
1633: }
1634: }
1635:
1636: // add collapsed path info
1637: if (_documentNavigator instanceof JTreeSortNavigator) {
1638: String[] paths = ((JTreeSortNavigator<?>) _documentNavigator)
1639: .getCollapsedPaths();
1640: for (String s : paths) {
1641: builder.addCollapsedPath(s);
1642: }
1643: }
1644:
1645: Iterable<File> exCp = getExtraClassPath();
1646: if (exCp != null) {
1647: for (File f : exCp) {
1648: builder.addClassPathFile(f);
1649: }
1650: }
1651: // else System.err.println("Project ClasspathVector is null!");
1652:
1653: // add build directory
1654: File bd = getBuildDirectory();
1655: if (bd != null)
1656: builder.setBuildDirectory(bd);
1657:
1658: // add working directory
1659: File wd = getWorkingDirectory(); // the value of the working directory to be stored in the project
1660: if (wd != null)
1661: builder.setWorkingDirectory(wd);
1662:
1663: // add jar main class
1664: File mainClass = getMainClass();
1665: if (mainClass != null)
1666: builder.setMainClass(mainClass);
1667:
1668: // add create jar file
1669: File createJarFile = getCreateJarFile();
1670: if (createJarFile != null)
1671: builder.setCreateJarFile(createJarFile);
1672:
1673: int createJarFlags = getCreateJarFlags();
1674: if (createJarFlags != 0)
1675: builder.setCreateJarFlags(createJarFlags);
1676:
1677: // add breakpoints and watches
1678: ArrayList<DebugBreakpointData> l = new ArrayList<DebugBreakpointData>();
1679: for (Breakpoint bp : getBreakpointManager().getRegions()) {
1680: l.add(bp);
1681: }
1682: builder.setBreakpoints(l);
1683: try {
1684: builder.setWatches(getDebugger().getWatches());
1685: } catch (DebugException de) { /* ignore, just don't store watches */
1686: }
1687:
1688: // add bookmarks
1689: builder.setBookmarks(getBookmarkManager().getRegions());
1690:
1691: return builder;
1692: }
1693:
1694: /** Writes the project profile augmented by usage info to specified file. Assumes DrJava is in project mode.
1695: * @param file where to save the project
1696: */
1697: public void saveProject(File file,
1698: Hashtable<OpenDefinitionsDocument, DocumentInfoGetter> info)
1699: throws IOException {
1700: ProjectProfile builder = _makeProjectProfile(file, info);
1701:
1702: // write to disk
1703: builder.write();
1704:
1705: // synchronized(_auxiliaryFiles) {
1706: // _auxiliaryFiles = new LinkedList<File>();
1707: // for (File f: builder.getAuxiliaryFiles()) { _auxiliaryFiles.add(f); }
1708: // }
1709:
1710: setFileGroupingState(makeProjectFileGroupingState(builder
1711: .getProjectRoot(), builder.getMainClass(), builder
1712: .getBuildDirectory(), builder.getWorkingDirectory(),
1713: file, builder.getSourceFiles(), builder
1714: .getAuxiliaryFiles(), builder.getClassPaths(),
1715: builder.getCreateJarFile(), builder.getCreateJarFlags()));
1716: }
1717:
1718: public void reloadProject(File file,
1719: Hashtable<OpenDefinitionsDocument, DocumentInfoGetter> info)
1720: throws IOException {
1721: boolean projChanged = isProjectChanged();
1722: ProjectProfile builder = _makeProjectProfile(file, info);
1723: _loadProject(builder);
1724: setProjectChanged(projChanged);
1725: }
1726:
1727: /** Parses the given project file and loads it int the document navigator and resets interactions pane. Assumes
1728: * preceding project if any has already been closed.
1729: *
1730: * @param projectFile The project file to parse
1731: */
1732: public void openProject(File projectFile) throws IOException,
1733: MalformedProjectFileException {
1734: _loadProject(ProjectFileParser.ONLY.parse(projectFile));
1735: }
1736:
1737: /** Loads the specified project into the document navigator and opens all of the files (if not already open).
1738: * Assumes that any prior project has been closed. Only runs in event thread.
1739: * @param projectFile The project file to parse
1740: */
1741: private void _loadProject(final ProjectFileIR ir)
1742: throws IOException {
1743:
1744: assert EventQueue.isDispatchThread();
1745:
1746: final DocFile[] srcFiles = ir.getSourceFiles();
1747: final DocFile[] auxFiles = ir.getAuxiliaryFiles();
1748: final File projectFile = ir.getProjectFile();
1749: final File projectRoot = ir.getProjectRoot();
1750: final File buildDir = ir.getBuildDirectory();
1751: final File workDir = ir.getWorkingDirectory();
1752: final File mainClass = ir.getMainClass();
1753: final Iterable<File> projectClassPaths = ir.getClassPaths();
1754: final File createJarFile = ir.getCreateJarFile();
1755: int createJarFlags = ir.getCreateJarFlags();
1756:
1757: // clear browser history
1758: getBrowserHistoryManager().clearRegions();
1759:
1760: // set breakpoints
1761: getBreakpointManager().clearRegions();
1762: for (DebugBreakpointData dbd : ir.getBreakpoints()) {
1763: try {
1764: getDebugger().toggleBreakpoint(
1765: getDocumentForFile(dbd.getFile()),
1766: dbd.getOffset(), dbd.getLineNumber(),
1767: dbd.isEnabled());
1768: } catch (DebugException de) { /* ignore, just don't add breakpoint */
1769: }
1770: }
1771:
1772: // set watches
1773: try {
1774: getDebugger().removeAllWatches();
1775: } catch (DebugException de) { /* ignore, just don't remove old watches */
1776: }
1777: for (DebugWatchData dwd : ir.getWatches()) {
1778: try {
1779: getDebugger().addWatch(dwd.getName());
1780: } catch (DebugException de) { /* ignore, just don't add watch */
1781: }
1782: }
1783:
1784: // set bookmarks
1785: getBookmarkManager().clearRegions();
1786: for (final DocumentRegion bm : ir.getBookmarks()) {
1787: final OpenDefinitionsDocument odd = getDocumentForFile(bm
1788: .getFile());
1789: getBookmarkManager().addRegion(
1790: new SimpleDocumentRegion(odd, odd.getFile(), bm
1791: .getStartOffset(), bm.getEndOffset()));
1792: }
1793:
1794: final String projfilepath = projectRoot.getCanonicalPath();
1795:
1796: // Get the list of documents that are still open
1797: // final List<OpenDefinitionsDocument> oldDocs = getOpenDefintionsDocuments();
1798: // final FileGroupingState oldState = _state;
1799:
1800: // Utilities.showDebug("openProject called with file " + projectFile);
1801:
1802: // Sets up the filters that cause documents to load in differentnsections of the tree. The names of these
1803: // sections are set from the methods such as getSourceBinTitle(). Changing this changes what is considered
1804: // source, aux, and external.
1805:
1806: List<Pair<String, INavigatorItemFilter<OpenDefinitionsDocument>>> l = new LinkedList<Pair<String, INavigatorItemFilter<OpenDefinitionsDocument>>>();
1807:
1808: l
1809: .add(new Pair<String, INavigatorItemFilter<OpenDefinitionsDocument>>(
1810: getSourceBinTitle(),
1811: new INavigatorItemFilter<OpenDefinitionsDocument>() {
1812: public boolean accept(
1813: OpenDefinitionsDocument d) {
1814: return d.inProjectPath();
1815: }
1816: }));
1817:
1818: l
1819: .add(new Pair<String, INavigatorItemFilter<OpenDefinitionsDocument>>(
1820: getAuxiliaryBinTitle(),
1821: new INavigatorItemFilter<OpenDefinitionsDocument>() {
1822: public boolean accept(
1823: OpenDefinitionsDocument d) {
1824: return d.isAuxiliaryFile();
1825: }
1826: }));
1827:
1828: l
1829: .add(new Pair<String, INavigatorItemFilter<OpenDefinitionsDocument>>(
1830: getExternalBinTitle(),
1831: new INavigatorItemFilter<OpenDefinitionsDocument>() {
1832: public boolean accept(
1833: OpenDefinitionsDocument d) {
1834: return !(d.inProject() || d
1835: .isAuxiliaryFile())
1836: || d.isUntitled();
1837: }
1838: }));
1839:
1840: IDocumentNavigator<OpenDefinitionsDocument> newNav = new AWTContainerNavigatorFactory<OpenDefinitionsDocument>()
1841: .makeTreeNavigator(projfilepath,
1842: getDocumentNavigator(), l);
1843:
1844: setDocumentNavigator(newNav);
1845:
1846: // synchronized(_auxiliaryFiles) {
1847: // _auxiliaryFiles.clear();
1848: // for (File file: auxFiles) { _auxiliaryFiles.add(file); }
1849: // }
1850:
1851: // Utilities.show("Project Root loaded into grouping state is " + projRoot);
1852:
1853: setFileGroupingState(makeProjectFileGroupingState(projectRoot,
1854: mainClass, buildDir, workDir, projectFile, srcFiles,
1855: auxFiles, projectClassPaths, createJarFile,
1856: createJarFlags));
1857:
1858: resetInteractions(getWorkingDirectory()); // Shutdown debugger and reset interactions pane in new working directory
1859:
1860: ArrayList<DocFile> projFiles = new ArrayList<DocFile>();
1861: DocFile active = null;
1862: ArrayList<DocumentRegion> expiredBookmarks = new ArrayList<DocumentRegion>();
1863: ArrayList<Breakpoint> expiredBreakpoints = new ArrayList<Breakpoint>();
1864: for (DocFile f : srcFiles) {
1865: if (f.lastModified() > f.getSavedModDate()) {
1866: for (DocumentRegion r : getBookmarkManager()
1867: .getRegions())
1868: if (r.getFile().equals(f))
1869: expiredBookmarks.add(r);
1870: for (Breakpoint r : getBreakpointManager().getRegions())
1871: if (r.getFile().equals(f))
1872: expiredBreakpoints.add(r);
1873: f.setSavedModDate(f.lastModified());
1874: }
1875: projFiles.add(f);
1876: }
1877: for (DocFile f : auxFiles) {
1878: if (f.lastModified() > f.getSavedModDate()) {
1879: for (DocumentRegion r : getBookmarkManager()
1880: .getRegions())
1881: if (r.getFile().equals(f))
1882: expiredBookmarks.add(r);
1883: for (Breakpoint r : getBreakpointManager().getRegions())
1884: if (r.getFile().equals(f))
1885: expiredBreakpoints.add(r);
1886: f.setSavedModDate(f.lastModified());
1887: }
1888: projFiles.add(f);
1889: }
1890: // Remove bookmarks and breakpoints for files that were modified outside of DrJava
1891: for (DocumentRegion r : expiredBookmarks)
1892: getBookmarkManager().removeRegion(r);
1893: for (Breakpoint r : expiredBreakpoints)
1894: getBreakpointManager().removeRegion(r);
1895:
1896: // Insert active file as last file on list.
1897: if (active != null)
1898: projFiles.add(active);
1899:
1900: // Utilities.showDebug("Project files are: " + projFiles);
1901:
1902: final List<OpenDefinitionsDocument> projDocs = getProjectDocuments(); // project source files
1903:
1904: // No files from the previous project (if any) can be open since it was already closed.
1905: // But all other files open at time this project is loaded are eligible for inclusion in the new project.
1906:
1907: if (!projDocs.isEmpty())
1908: // Utilities.invokeAndWait(new SRunnable() {
1909: // public void run() {
1910: for (OpenDefinitionsDocument d : projDocs) {
1911: try {
1912: final String path = fixPathForNavigator(d.getFile()
1913: .getCanonicalPath());
1914: _documentNavigator.refreshDocument(d, path); // this operation must run in event thread
1915: } catch (IOException e) { /* Do nothing */
1916: }
1917: }
1918: // }
1919: // });
1920:
1921: // Utilities.showDebug("Preparing to refresh navigator GUI");
1922: // call on the GUI to finish up by opening the files and making necessary gui component changes
1923: final DocFile[] filesToOpen = projFiles
1924: .toArray(new DocFile[projFiles.size()]);
1925: _notifier.projectOpened(projectFile, new FileOpenSelector() {
1926: public File[] getFiles() {
1927: return filesToOpen;
1928: }
1929: });
1930:
1931: //Set active document from project file
1932: // if(active != null) { //TEMP
1933: // setActiveDocument(projDocs.get(projDocs.size() - 1));
1934: // }
1935: //OpenDefinitionsDocument.getCanonicalPath()
1936: //search for active document within opendefdocs
1937: // Utilities.show("Setting Active Document...");
1938: // if(active != null) {
1939: // String path = projFiles.get(projFiles.size() - 1).getCanonicalPath();
1940: // Utilities.show("Active document path: " + path);
1941: // for(OpenDefinitionsDocument doc: projDocs) {
1942: // Utilities.show("Searching path: " + doc.getCanonicalPath());
1943: // if(doc.getCanonicalPath().compareTo(path) == 0) {
1944: // setActiveDocument(doc);
1945: // Utilities.show("New active document set");
1946: // break;
1947: // }
1948: // }
1949: // }
1950:
1951: if (_documentNavigator instanceof JTreeSortNavigator)
1952: ((JTreeSortNavigator<?>) _documentNavigator)
1953: .collapsePaths(ir.getCollapsedPaths());
1954: }
1955:
1956: /** Performs any needed operations on the model after project files have been closed. This method is not
1957: * responsible for closing any filesl both the files in the project and the project file have already been
1958: * closed (by MainFrame._closeProject()). Resets interations unless suppressReset is true.
1959: */
1960: public void closeProject(boolean suppressReset) {
1961: setDocumentNavigator(new AWTContainerNavigatorFactory<OpenDefinitionsDocument>()
1962: .makeListNavigator(getDocumentNavigator()));
1963: setFileGroupingState(makeFlatFileGroupingState());
1964:
1965: if (!suppressReset)
1966: resetInteractions(getWorkingDirectory());
1967: _notifier.projectClosed();
1968: }
1969:
1970: /** If the document is untitled, brings it to the top so that the
1971: * user will know which is being saved.
1972: */
1973: public void aboutToSaveFromSaveAll(OpenDefinitionsDocument doc) {
1974: if (doc.isUntitled())
1975: setActiveDocument(doc);
1976: }
1977:
1978: /** Closes an open definitions document, prompting to save if the document has been changed. Returns whether
1979: * the file was successfully closed. Also ensures the invariant that there is always at least
1980: * one open document holds by creating a new file if necessary.
1981: * @return true if the document was closed
1982: */
1983: public boolean closeFile(OpenDefinitionsDocument doc) {
1984: List<OpenDefinitionsDocument> list = new LinkedList<OpenDefinitionsDocument>();
1985: list.add(doc);
1986: return closeFiles(list);
1987: }
1988:
1989: /** Attempts to close all open documents. Also ensures the invariant that there is always at least
1990: * one open document holds by creating a new file if necessary. Resets interactions iff operation succeeds.
1991: * @return true if all documents were closed
1992: */
1993: public boolean closeAllFiles() {
1994: List<OpenDefinitionsDocument> docs = getOpenDefinitionsDocuments();
1995: boolean res = closeFiles(docs);
1996: if (res) {
1997: // _log.log("Resetting interactions pane to use " + getWorkingDirectory() + " as working directory");
1998: resetInteractions(getWorkingDirectory());
1999: }
2000: return res;
2001: }
2002:
2003: /** This function closes a group of files assuming that the files are contiguous in the enumeration
2004: * provided by the document navigator. This assumption is used in selecting which remaining document
2005: * (if any) to activate.
2006: * <p>
2007: * The corner cases in which the file that is being closed had been externally
2008: * deleted have been addressed in a few places, namely DefaultGlobalModel.canAbandonFile()
2009: * and MainFrame.ModelListener.canAbandonFile(). If the DefinitionsDocument for the
2010: * OpenDefinitionsDocument being closed is not in the cache (see model.cache.DocumentCache)
2011: * then it is closed without prompting the user to save it. If it is in the cache, then
2012: * we can successfully notify the user that the file is selected for closing and ask whether to
2013: * saveAs, close, or cancel.
2014: * @param docs the list od OpenDefinitionsDocuments to close
2015: * @return whether all files were closed
2016: */
2017: public boolean closeFiles(List<OpenDefinitionsDocument> docs) {
2018: if (docs.size() == 0)
2019: return true;
2020:
2021: _log.log("closeFiles(" + docs + ") called");
2022: /* Force the user to save or discard all modified files in docs */
2023: for (OpenDefinitionsDocument doc : docs) {
2024: if (!doc.canAbandonFile())
2025: return false;
2026: }
2027:
2028: /* If all files are being closed, create a new file before starting in order to have a potentially active file
2029: * that is not in the list of closing files. */
2030: if (docs.size() == getOpenDefinitionsDocumentsSize())
2031: newFile();
2032:
2033: /* Set the active document to the document just after the last document or the document just before the first
2034: * document in docs. The new file created above (if necessary) does not appear in docs. */
2035: _ensureNotActive(docs);
2036:
2037: // Close the files in docs.
2038: for (OpenDefinitionsDocument doc : docs) {
2039: closeFileWithoutPrompt(doc);
2040: }
2041: return true;
2042: }
2043:
2044: /** Helper for closeFile. This method was the closeFile(...) method before projects were added to DrJava. */
2045: protected boolean closeFileHelper(OpenDefinitionsDocument doc) {
2046: // System.err.println("closing " + doc);
2047: boolean canClose = doc.canAbandonFile();
2048: if (canClose)
2049: return closeFileWithoutPrompt(doc);
2050: return false;
2051: }
2052:
2053: /** Closes an open definitions document, without prompting to save if the document has been changed. Returns
2054: * whether the file was successfully closed. NOTE: This method should not be called unless it can be
2055: * absolutely known that the document being closed is not the active document. The closeFile() method in
2056: * SingleDisplayModel ensures that a new active document is set, but closeFileWithoutPrompt is not.
2057: * @return true if the document was closed.
2058: */
2059: public boolean closeFileWithoutPrompt(
2060: final OpenDefinitionsDocument doc) {
2061: // new Exception("Closed document " + doc).printStackTrace();
2062:
2063: _log.log("closeFileWithoutPrompt(" + doc
2064: + ") called; getRawFile() = " + doc.getRawFile());
2065: _log.log("_documentsRepos = " + _documentsRepos);
2066: boolean found;
2067: synchronized (_documentsRepos) {
2068: found = (_documentsRepos.remove(doc.getRawFile()) != null);
2069: }
2070:
2071: if (!found) {
2072: _log.log("Cannot close " + doc + "; not found!");
2073: return false;
2074: }
2075:
2076: // remove regions for this file
2077: doc.getBreakpointManager().clearRegions();
2078: doc.getBookmarkManager().clearRegions();
2079: for (RegionManager<MovingDocumentRegion> rm : doc
2080: .getFindResultsManagers())
2081: rm.clearRegions();
2082: doc.getBrowserHistoryManager().clearRegions();
2083:
2084: // if the document was an auxiliary file, remove it from the list
2085: if (doc.isAuxiliaryFile()) {
2086: removeAuxiliaryFile(doc);
2087: }
2088:
2089: Utilities.invokeLater(new SRunnable() {
2090: public void run() {
2091: _documentNavigator.removeDocument(doc);
2092: } // this operation must run in event thread
2093: });
2094: _notifier.fileClosed(doc);
2095: doc.close();
2096: return true;
2097: }
2098:
2099: /** Closes all open documents. This operation can be cancelled by the user since it
2100: * checks if all files can be abandoned BEFORE it actually modifies the project state.
2101: * @return {@code false} if the user cancelled
2102: */
2103: public boolean closeAllFilesOnQuit() {
2104:
2105: List<OpenDefinitionsDocument> docs = getOpenDefinitionsDocuments();
2106:
2107: // first see if the user wants to cancel on any of them
2108: OpenDefinitionsDocument retainedDoc = null;
2109:
2110: for (OpenDefinitionsDocument doc : docs) {
2111: if (!doc.canAbandonFile()) {
2112: return false;
2113: }
2114: }
2115:
2116: // user did not want to cancel, close all of them
2117: // All files are being closed, create a new file before starting in order to have
2118: // a potentially active file that is not in the list of closing files.
2119: newFile();
2120:
2121: // Set the active document to the document just after the last document or the document just before the
2122: // first document in docs. A new file does not appear in docs.
2123: _ensureNotActive(docs);
2124:
2125: // Close the files in docs.
2126: for (OpenDefinitionsDocument doc : docs) {
2127: closeFileWithoutPrompt(doc);
2128: }
2129:
2130: return true;
2131: }
2132:
2133: /** Exits the program. Only quits if all documents are successfully closed. */
2134: public void quit() {
2135: quit(false);
2136: }
2137:
2138: /** Halts the program immediately. */
2139: public void forceQuit() {
2140: quit(true);
2141: }
2142:
2143: /** Exits the program. If force is true, quits regardless of whether all documents are successfully closed. */
2144: private void quit(boolean force) {
2145: // _log.log("quit(" + force + ") called");
2146: try {
2147: if (!force && !closeAllFilesOnQuit())
2148: return;
2149:
2150: /* [ 1478796 ] DrJava Does Not Shut Down With Project Open. On HP tc1100 and Toshiba Portege tablet PCs, there
2151: * appears to be a problem in a shutdown hook, presumably the RMI shutdown hook. Shutdown hooks get executed in
2152: * Runtime.exit (to which System.exit delegates), and if a shutdown hook does not complete, the VM does not shut
2153: * down. The difference between Runtime.halt and Runtime.exit is that Runtime.exit runs the shutdown hooks and
2154: * the finalizers (if Runtime.runFinalizersOnExit(true) has been called); then it calls Runtime.halt. The RMI
2155: * hooks are potentially important in running unit test that repeatedly start and stop DrJava, so we only invoke
2156: * Runtime.halt if our attempt to exit times out.
2157: */
2158:
2159: shutdown(force);
2160: } catch (Throwable t) {
2161: shutdown(true); /* force exit anyway */
2162: }
2163: }
2164:
2165: /* Terminates DrJava via System.exit with Runtime.halt as a backup if the former gets hung up. */
2166: private void shutdown(boolean force) {
2167: if (force)
2168: Runtime.getRuntime().halt(0);
2169:
2170: dispose(); // kills interpreter and cleans up RMI hooks in the slave JVM
2171:
2172: if (DrJava.getConfig().getSetting(
2173: OptionConstants.DRJAVA_USE_FORCE_QUIT)) {
2174: Runtime.getRuntime().halt(0); // force DrJava to exit
2175: }
2176:
2177: Thread monitor = new Thread(new Runnable() {
2178: public void run() {
2179: try {
2180: Thread.sleep(2000);
2181: } catch (InterruptedException e) { /* proceed */
2182: }
2183: Runtime.getRuntime().halt(0); // force DrJava to exit if it still alive
2184: }
2185: });
2186: monitor.setDaemon(true);
2187: monitor.start();
2188: System.exit(0);
2189: }
2190:
2191: /** Prepares this model to be thrown away. Never called outside of tests. This version ignores the slave JVM. */
2192: public void dispose() {
2193: synchronized (_documentsRepos) {
2194: _documentsRepos.clear();
2195: }
2196: Utilities.invokeLater(new SRunnable() {
2197: public void run() {
2198: _documentNavigator.clear();
2199: } // this operation must run in event thread
2200: });
2201: // Only remove listeners after pending events have completed
2202: SwingUtilities.invokeLater(new Runnable() {
2203: public void run() {
2204: _notifier.removeAllListeners();
2205: }
2206: });
2207: }
2208:
2209: /** Disposes of external resources. This is a no op in AbstractGlobalModel. */
2210: public void disposeExternalResources() { /* no op */
2211: }
2212:
2213: /** Gets the document for the specified file; may involve opening the file. */
2214: public OpenDefinitionsDocument getDocumentForFile(File file)
2215: throws IOException {
2216: // Check if this file is already open
2217: OpenDefinitionsDocument doc = _getOpenDocument(file);
2218: if (doc == null) {
2219: // If not, open and return it
2220: final File f = file;
2221: FileOpenSelector selector = new FileOpenSelector() {
2222: public File[] getFiles() {
2223: return new File[] { f };
2224: }
2225: };
2226: try {
2227: doc = openFile(selector);
2228: } catch (AlreadyOpenException e) {
2229: doc = e.getOpenDocument();
2230: } catch (OperationCanceledException e) {
2231: throw new UnexpectedException(e); /* Cannot happen */
2232: }
2233: }
2234: return doc;
2235: }
2236:
2237: /** Iterates over OpenDefinitionsDocuments, looking for this file.
2238: * TODO: This is not very efficient!
2239: */
2240: public boolean isAlreadyOpen(File file) {
2241: return (_getOpenDocument(file) != null);
2242: }
2243:
2244: /** Returns the OpenDefinitionsDocument corresponding to the INavigatorItem/DefinitionsDocument passed in.
2245: * @param doc the searched for Document
2246: * @return its corresponding OpenDefinitionsDocument
2247: */
2248: public OpenDefinitionsDocument getODDForDocument(
2249: AbstractDocumentInterface doc) {
2250: /** This function needs to be phased out altogether; the goal is for the OpenDefinitionsDocument
2251: * to also function as its own Document, so this function will be useless
2252: */
2253: if (doc instanceof OpenDefinitionsDocument)
2254: return (OpenDefinitionsDocument) doc;
2255: if (doc instanceof DefinitionsDocument)
2256: return ((DefinitionsDocument) doc).getOpenDefDoc();
2257: throw new IllegalStateException(
2258: "Could not get the OpenDefinitionsDocument for Document: "
2259: + doc);
2260: }
2261:
2262: /** Gets a DocumentIterator to allow navigating through open Swing Documents. */
2263: public DocumentIterator getDocumentIterator() {
2264: return this ;
2265: }
2266:
2267: /** Returns the ODD preceding the given document in the document list.
2268: * @param d the current Document
2269: * @return the next Document
2270: */
2271: public OpenDefinitionsDocument getNextDocument(
2272: OpenDefinitionsDocument d) {
2273: OpenDefinitionsDocument nextdoc = null; // irrelevant initialization required by javac
2274: // try {
2275: OpenDefinitionsDocument doc = getODDForDocument(d);
2276: nextdoc = _documentNavigator.getNext(doc);
2277: if (nextdoc == doc)
2278: nextdoc = _documentNavigator.getFirst(); // wrap around if necessary
2279: OpenDefinitionsDocument res = getNextDocHelper(nextdoc);
2280: // Utilities.showDebug("nextDocument(" + d + ") = " + res);
2281: return res;
2282: // }
2283: // catch(DocumentClosedException dce) { return getNextDocument(nextdoc); }
2284: }
2285:
2286: private OpenDefinitionsDocument getNextDocHelper(
2287: OpenDefinitionsDocument nextdoc) {
2288: if (nextdoc.isUntitled() || nextdoc.verifyExists())
2289: return nextdoc;
2290: // Note: verifyExists prompts user for location of the file if it is not found
2291:
2292: // cannot find nextdoc; move on to next document
2293: return getNextDocument(nextdoc);
2294: }
2295:
2296: /** Returns the ODD preceding the given document in the document list.
2297: * @param d the current Document
2298: * @return the previous Document
2299: */
2300: public OpenDefinitionsDocument getPrevDocument(
2301: OpenDefinitionsDocument d) {
2302: OpenDefinitionsDocument prevdoc = null; // irrelevant initialization required by javac
2303: // try {
2304: OpenDefinitionsDocument doc = getODDForDocument(d);
2305: prevdoc = _documentNavigator.getPrevious(doc);
2306: if (prevdoc == doc)
2307: prevdoc = _documentNavigator.getLast(); // wrap around if necessary
2308: return getPrevDocHelper(prevdoc);
2309: // }
2310: // catch(DocumentClosedException dce) { return getPrevDocument(prevdoc); }
2311: }
2312:
2313: private OpenDefinitionsDocument getPrevDocHelper(
2314: OpenDefinitionsDocument prevdoc) {
2315: if (prevdoc.isUntitled() || prevdoc.verifyExists())
2316: return prevdoc;
2317: // Note: verifyExists() prompts user for location of prevdoc
2318:
2319: // cannot find prevdoc; move on to preceding document
2320: return getPrevDocument(prevdoc);
2321: }
2322:
2323: public int getDocumentCount() {
2324: return _documentsRepos.size();
2325: }
2326:
2327: /** Returns a new collection of all documents currently open for editing. This is equivalent to the results of
2328: * getDocumentForFile for the set of all files for which isAlreadyOpen returns true.
2329: * @return a random-access List of the open definitions documents..
2330: */
2331: public List<OpenDefinitionsDocument> getOpenDefinitionsDocuments() {
2332: synchronized (_documentsRepos) {
2333: ArrayList<OpenDefinitionsDocument> docs = new ArrayList<OpenDefinitionsDocument>(
2334: _documentsRepos.size());
2335: for (OpenDefinitionsDocument doc : _documentsRepos.values()) {
2336: docs.add(doc);
2337: }
2338: return docs;
2339: }
2340: }
2341:
2342: /* Returns a sorted (by time of insertion) collection of all open documents. */
2343: public List<OpenDefinitionsDocument> getSortedOpenDefinitionsDocuments() {
2344: return getOpenDefinitionsDocuments();
2345: }
2346:
2347: /** @return the size of the collection of OpenDefinitionsDocuments */
2348: public int getOpenDefinitionsDocumentsSize() {
2349: synchronized (_documentsRepos) {
2350: return _documentsRepos.size();
2351: }
2352: }
2353:
2354: /** @return true if all open documents are in sync with their primary class files. */
2355: public boolean hasOutOfSyncDocuments() {
2356: return hasOutOfSyncDocuments(getOpenDefinitionsDocuments());
2357: }
2358:
2359: public boolean hasOutOfSyncDocuments(
2360: List<OpenDefinitionsDocument> lod) {
2361: for (OpenDefinitionsDocument doc : lod) {
2362: if (doc.isSourceFile() && !doc.checkIfClassFileInSync())
2363: return true;
2364: }
2365: return false;
2366: }
2367:
2368: /** Set the indent tab size for all definitions documents.
2369: * @param indent the number of spaces to make per level of indent
2370: */
2371: void setDefinitionsIndent(int indent) {
2372: for (OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
2373: doc.setIndent(indent);
2374: }
2375: }
2376:
2377: /** A degenerate operation since this has no slave JVM and no interactions model. */
2378: public void resetInteractions(File wd) { /* do nothing */
2379: }
2380:
2381: /** A degenerate operation since this has no slave JVM and no interactions model. */
2382: public void resetInteractions(File wd, boolean forceReset) { /* do nothing */
2383: }
2384:
2385: /** Resets the console. Fires consoleReset() event. */
2386: public void resetConsole() {
2387: _consoleDoc.reset("");
2388: _notifier.consoleReset();
2389: }
2390:
2391: /** throw new UnsupportedOperationException */
2392: public void interpretCurrentInteraction() {
2393: throw new UnsupportedOperationException(
2394: "AbstractGlobalModel does not support interactions");
2395: }
2396:
2397: /** throws UnsupportedOperationException */
2398: public void loadHistory(FileOpenSelector selector)
2399: throws IOException {
2400: throw new UnsupportedOperationException(
2401: "AbstractGlobalModel does not support interactions");
2402: }
2403:
2404: /** throws UnsupportedOperationException */
2405: public InteractionsScriptModel loadHistoryAsScript(
2406: FileOpenSelector selector) throws IOException,
2407: OperationCanceledException {
2408: throw new UnsupportedOperationException(
2409: "AbstractGlobalModel does not support interactions");
2410: }
2411:
2412: /** throws UnsupportedOperationException */
2413: public void clearHistory() {
2414: throw new UnsupportedOperationException(
2415: "AbstractGlobalModel does not support interactions");
2416: }
2417:
2418: /** throws UnsupportedOperationException */
2419: public void saveHistory(FileSaveSelector selector)
2420: throws IOException {
2421: throw new UnsupportedOperationException(
2422: "AbstractGlobalModel does not support interactions");
2423: }
2424:
2425: /** throws UnsupportedOperationException */
2426: public void saveHistory(FileSaveSelector selector,
2427: String editedVersion) throws IOException {
2428: throw new UnsupportedOperationException(
2429: "AbstractGlobalModel does not support interactions");
2430: }
2431:
2432: /** Returns the entire history as a String with semicolons as needed. */
2433: public String getHistoryAsStringWithSemicolons() {
2434: throw new UnsupportedOperationException(
2435: "AbstractGlobalModel does not support interactions");
2436: }
2437:
2438: /** Throws UnsupportedOperationException */
2439: public String getHistoryAsString() {
2440: throw new UnsupportedOperationException(
2441: "AbstractGlobalModel does not support interactions");
2442: }
2443:
2444: /** Registers OptionListeners. Factored out code from the two constructor. */
2445: private void _registerOptionListeners() {
2446: // // Listen to any relevant config options
2447: // DrJava.getConfig().addOptionListener(EXTRA_CLASSPATH, new ExtraClasspathOptionListener());
2448:
2449: DrJava.getConfig().addOptionListener(BACKUP_FILES,
2450: new BackUpFileOptionListener());
2451: Boolean makeBackups = DrJava.getConfig().getSetting(
2452: BACKUP_FILES);
2453: FileOps.DefaultFileSaver.setBackupsEnabled(makeBackups
2454: .booleanValue());
2455:
2456: // DrJava.getConfig().addOptionListener(ALLOW_PRIVATE_ACCESS, new OptionListener<Boolean>() {
2457: // public void optionChanged(OptionEvent<Boolean> oce) {
2458: // getInteractionsModel().setPrivateAccessible( oce.value.booleanValue());
2459: // }
2460: // });
2461: }
2462:
2463: /** Appends a string to the given document using a particular attribute set.
2464: * Also waits for a small amount of time (WRITE_DELAY) to prevent any one
2465: * writer from flooding the model with print calls to the point that the
2466: * user interface could become unresponsive.
2467: * @param doc Document to append to
2468: * @param s String to append to the end of the document
2469: * @param style the style to print with
2470: */
2471: protected void _docAppend(ConsoleDocument doc, String s,
2472: String style) {
2473: /** A lock object to prevent print calls from flooding the JVM, ensuring the UI remains responsive. */
2474: final Object systemWriterLock = new Object();
2475:
2476: synchronized (systemWriterLock) {
2477: try {
2478: doc.insertBeforeLastPrompt(s, style);
2479: systemWriterLock.wait(WRITE_DELAY); // Wait to prevent being print flooding
2480: } catch (InterruptedException e) { /* Ignore and resume */
2481: }
2482: }
2483: }
2484:
2485: /** Prints System.out to the DrJava console. */
2486: public void systemOutPrint(String s) {
2487: _docAppend(_consoleDoc, s, ConsoleDocument.SYSTEM_OUT_STYLE);
2488: }
2489:
2490: /** Prints System.err to the DrJava console. */
2491: public void systemErrPrint(String s) {
2492: _docAppend(_consoleDoc, s, ConsoleDocument.SYSTEM_ERR_STYLE);
2493: }
2494:
2495: /** Prints the given string to the DrJava console as an echo of System.in */
2496: public void systemInEcho(String s) {
2497: _docAppend(_consoleDoc, s, ConsoleDocument.SYSTEM_IN_STYLE);
2498: }
2499:
2500: /** throws UnsupportedOperationException */
2501: public void printDebugMessage(String s) {
2502: throw new UnsupportedOperationException(
2503: "AbstractGlobalModel does not support debugging");
2504: }
2505:
2506: /** throw new UnsupportedOperationException */
2507: public void waitForInterpreter() {
2508: throw new UnsupportedOperationException(
2509: "AbstractGlobalModel does not support interactions");
2510: }
2511:
2512: /** throws new UnsupportedOperationException */
2513: public Iterable<File> getInteractionsClassPath() {
2514: throw new UnsupportedOperationException(
2515: "AbstractGlobalModel does not support interactions");
2516: }
2517:
2518: /** Returns a project's extra classpaths; empty for FlatFileGroupingState
2519: * @return The classpath entries loaded along with the project
2520: */
2521: public Iterable<File> getExtraClassPath() {
2522: return _state.getExtraClassPath();
2523: }
2524:
2525: /** Sets the set of classpath entries to use as the projects set of classpath entries. This is normally used by the
2526: * project preferences..
2527: */
2528: public void setExtraClassPath(Iterable<File> cp) {
2529: _state.setExtraClassPath(cp);
2530: setClassPathChanged(true);
2531: //System.out.println("Setting project classpath to: " + cp);
2532: }
2533:
2534: /** Gets an array of all sourceRoots for the open definitions documents, without duplicates. */
2535: public Iterable<File> getSourceRootSet() {
2536: Set<File> roots = new LinkedHashSet<File>();
2537:
2538: for (OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
2539: try {
2540: if (!doc.isUntitled()) {
2541: File root = doc.getSourceRoot();
2542: if (root != null)
2543: roots.add(root); // Can't create duplicate entries in a Set
2544: }
2545: } catch (InvalidPackageException e) {
2546: // Utilities.show("InvalidPackageException in getSourceRootSet");
2547: /* file has invalid package statement; ignore it */
2548: }
2549: }
2550: return roots;
2551: }
2552:
2553: // /** Return the absolute path of the file with the given index, or "(untitled)" if no file exists. */
2554: // public String getDisplayFullPath(int index) {
2555: // OpenDefinitionsDocument doc = getOpenDefinitionsDocuments().get(index);
2556: // if (doc == null) throw new RuntimeException( "Document not found with index " + index);
2557: // return doc.getDisplayFullPath();
2558: // }
2559:
2560: /** throws UnsupportedOperationException */
2561: public Debugger getDebugger() {
2562: // throw new UnsupportedOperationException("AbstractGlobalModel does not support debugging");
2563: return NoDebuggerAvailable.ONLY;
2564: }
2565:
2566: /** throws UnsupportedOperationException */
2567: public int getDebugPort() throws IOException {
2568: throw new UnsupportedOperationException(
2569: "AbstractGlobalModel does not support debugging");
2570: }
2571:
2572: /** Checks if any open definitions documents have been modified since last being saved.
2573: * @return whether any documents have been modified
2574: */
2575: public boolean hasModifiedDocuments() {
2576: return hasModifiedDocuments(getOpenDefinitionsDocuments());
2577: }
2578:
2579: /** Checks if any given documents have been modified since last being saved.
2580: * @return whether any documents have been modified
2581: */
2582: public boolean hasModifiedDocuments(
2583: List<OpenDefinitionsDocument> lod) {
2584: for (OpenDefinitionsDocument doc : lod) {
2585: if (doc.isModifiedSinceSave())
2586: return true;
2587: }
2588: return false;
2589: }
2590:
2591: /** Checks if any open definitions documents are untitled.
2592: * @return whether any documents are untitled
2593: */
2594: public boolean hasUntitledDocuments() {
2595: for (OpenDefinitionsDocument doc : getOpenDefinitionsDocuments()) {
2596: if (doc.isUntitled())
2597: return true;
2598: }
2599: return false;
2600: }
2601:
2602: /** Searches for a file with the given name on the current source roots and the augmented classpath.
2603: * @param fileName name of the source file to look for
2604: * @return the file corresponding to the given name, or null if it cannot be found
2605: */
2606: public File getSourceFile(String fileName) {
2607: Iterable<File> sourceRoots = getSourceRootSet();
2608: for (File s : sourceRoots) {
2609: File f = _getSourceFileFromPath(fileName, s);
2610: if (f != null)
2611: return f;
2612: }
2613: Vector<File> sourcepath = DrJava.getConfig().getSetting(
2614: OptionConstants.DEBUG_SOURCEPATH);
2615: return findFileInPaths(fileName, sourcepath);
2616: }
2617:
2618: /** Searches for a file with the given name on the provided paths. Returns null if the file is not found.
2619: * @param fileName Name of the source file to look for
2620: * @param paths An array of directories to search
2621: * @return the file if it is found, or null otherwise
2622: */
2623: public File findFileInPaths(String fileName, Iterable<File> paths) {
2624: for (File p : paths) {
2625: File f = _getSourceFileFromPath(fileName, p);
2626: if (f != null)
2627: return f;
2628: }
2629: return null;
2630: }
2631:
2632: /** Gets the file named filename from the given path, if it exists. Returns null if it's not there.
2633: * @param filename the file to look for
2634: * @param path the path to look for it in
2635: * @return the file if it exists
2636: */
2637: private File _getSourceFileFromPath(String fileName, File path) {
2638: String root = path.getAbsolutePath();
2639: File f = new File(root + System.getProperty("file.separator")
2640: + fileName);
2641: return f.exists() ? f : null;
2642: }
2643:
2644: private static volatile int ID_COUNTER = 0; /* Seed for assigning id numbers to OpenDefinitionsDocuments */
2645:
2646: // ---------- ConcreteRegionManager inner class -------
2647: /** Simple region manager for the entire model. Follows readers/writers locking protocol of EventNotifier. */
2648: static class ConcreteRegionManager<R extends DocumentRegion>
2649: extends EventNotifier<RegionManagerListener<R>> implements
2650: RegionManager<R> {
2651: /** Vector of regions. Primitive operations are thread safe. */
2652: protected volatile Vector<R> _regions = new Vector<R>();
2653: protected volatile R _current = null;
2654: protected volatile int _maxSize;
2655:
2656: /** Create a new ConcreteRegionManager with the specified maximum size.
2657: * @param size maximum number of regions that can be stored in this manager.
2658: */
2659: public ConcreteRegionManager(int size) {
2660: _maxSize = size;
2661: }
2662:
2663: /** Create a new ConcreteRegionManager without maximum size. */
2664: public ConcreteRegionManager() {
2665: this (0);
2666: }
2667:
2668: /** Returns the region in this manager at the given offset, or null if one does not exist.
2669: * @param odd the document
2670: * @param offset the offset in the document
2671: * @return the DocumentRegion at the given line number, or null if it does not exist.
2672: */
2673: public R getRegionAt(OpenDefinitionsDocument odd, int offset) {
2674: for (R r : _regions) {
2675: if (r.getDocument().equals(odd)
2676: && offset >= r.getStartOffset()
2677: && offset <= r.getEndOffset())
2678: return r;
2679: }
2680: return null;
2681: }
2682:
2683: /** Get the DocumentRegion that is stored in this RegionsTreePanel overlapping the area for the given document,
2684: * or null if it doesn't exist.
2685: * @param odd the document
2686: * @param startOffset the start offset
2687: * @param endOffset the end offset
2688: * @return the DocumentRegion or null
2689: */
2690: public R getRegionOverlapping(OpenDefinitionsDocument odd,
2691: int startOffset, int endOffset) {
2692: for (R r : _regions) {
2693: if (!(r.getDocument().equals(odd))) {
2694: continue;
2695: }
2696:
2697: if ((r.getStartOffset() >= startOffset && r
2698: .getEndOffset() <= endOffset)
2699: || // r contained in startOffset-endOffset
2700: (r.getStartOffset() <= startOffset && r
2701: .getEndOffset() >= endOffset) || // startOffset-endOffset contained in r
2702: (r.getStartOffset() >= startOffset && r
2703: .getStartOffset() <= endOffset) || // r starts within startOffset-endOffset
2704: (r.getEndOffset() >= startOffset && r
2705: .getEndOffset() <= endOffset)) { // r ends within startOffset-endOffset
2706: // already there
2707: return r;
2708: }
2709: }
2710: // not found
2711: return null;
2712: }
2713:
2714: /** @return the index of the region in the vector, or -1 if not found. Uses ==. */
2715: protected int getIndexOf(R region) {
2716: int index = 0;
2717: for (R r : _regions) {
2718: if (region == r)
2719: return index;
2720: else
2721: ++index;
2722: }
2723: return -1;
2724: }
2725:
2726: /** Add the supplied DocumentRegion to the manager. Only runs in event thread after initialization?
2727: * @param region the DocumentRegion to be inserted into the manager
2728: * @param index the index at which the DocumentRegion was inserted
2729: */
2730: public void addRegion(final R region) {
2731: int index = getIndexOf(_current);
2732: // only add if current, previous, and next are not already the region; prevents trivial duplicates
2733: if (!region.equals(_current)
2734: && (index == _regions.size() - 1 || !region
2735: .equals(_regions.get(index + 1)))
2736: && (index <= 0 || !region.equals(_regions
2737: .get(index - 1)))) {
2738: if ((_current != null) && (index >= 0))
2739: _regions.add(index + 1, region);
2740: else
2741: _regions.add(region);
2742:
2743: _current = region;
2744: final int regionIndex = getIndexOf(region);
2745: final String stackTrace = StringOps.getStackTrace();
2746:
2747: // notify. invokeLater unnecessary if it only runs in the event thread
2748: Utilities.invokeLater(new Runnable() {
2749: public void run() {
2750: _lock.startRead();
2751: try {
2752: for (RegionManagerListener<R> l : _listeners) {
2753: l.regionAdded(region, regionIndex);
2754: }
2755: } finally {
2756: _lock.endRead();
2757: }
2758: }
2759: });
2760:
2761: // remove region if necessary
2762: shrinkManager();
2763: } else {
2764: // if next was the region to be added, make that the current region
2765: if ((index < _regions.size() - 1)
2766: && (region.equals(_regions.get(index + 1))))
2767: nextCurrentRegion();
2768: // if previous was the region to be added, make that the current region
2769: else if ((index > 0)
2770: && (region.equals(_regions.get(index - 1))))
2771: prevCurrentRegion();
2772: }
2773: }
2774:
2775: /** Remove regions more recent than the current region. */
2776: protected void removeMoreRecentThanCurrent() {
2777: if (_current != null) {
2778: int index = getIndexOf(_current);
2779: if (index < 0)
2780: return;
2781: while (index < _regions.size() - 1) {
2782: removeRegion(_regions.lastElement());
2783: } // remove last element
2784: }
2785: }
2786:
2787: /** Remove regions if there are more than the maximum number allowed. Typically used to remove one region. */
2788: protected void shrinkManager() {
2789: if (_maxSize > 0) {
2790: int size;
2791: while ((size = _regions.size()) > _maxSize) {
2792: int index = getIndexOf(_current);
2793: if (index <= (size - 1) / 2) {
2794: // in first half, remove last element; distinct from index unless _maxSize = 1
2795: _regions.remove(size - 1);
2796: } else {
2797: // in second half, remove first element; distinct from index
2798: _regions.remove(0);
2799: }
2800: }
2801: }
2802: }
2803:
2804: /** Remove the given DocumentRegion from the manager.
2805: * @param region the DocumentRegion to be removed.
2806: */
2807: public void removeRegion(final R region) {
2808: // if we're removing the current region, select a more recent region, if available
2809: // if a more recent region is not available, select a less recent region, if available
2810: // if a less recent region is not available either, set to null
2811: final R cur = _current; // so we can verify if _current got changed
2812: if (region == cur) {
2813: if (nextCurrentRegion().equals(cur)) {
2814: if (prevCurrentRegion().equals(cur)) {
2815: _current = null;
2816: }
2817: }
2818: }
2819: for (int i = 0; i < _regions.size(); ++i) {
2820: if (region == _regions.get(i)) {
2821: _regions.remove(i);
2822: break;
2823: }
2824: }
2825:
2826: // notify
2827: Utilities.invokeLater(new Runnable() {
2828: public void run() {
2829: _lock.startRead();
2830: try {
2831: for (RegionManagerListener<R> l : _listeners) {
2832: l.regionRemoved(region);
2833: }
2834: } finally {
2835: _lock.endRead();
2836: }
2837: }
2838: });
2839: }
2840:
2841: /** @return a Vector<R> containing the DocumentRegion objects in this mangager. */
2842: public Vector<R> getRegions() {
2843: return _regions;
2844: }
2845:
2846: /** Tells the manager to remove all regions. */
2847: public void clearRegions() {
2848: _current = null;
2849: while (_regions.size() > 0) {
2850: removeRegion(_regions.get(0));
2851: }
2852: }
2853:
2854: /** @return the current region or null if none selected */
2855: public R getCurrentRegion() {
2856: if (!_regions.contains(_current))
2857: _current = null;
2858: return _current;
2859: }
2860:
2861: /** @return the index of the current region or -1 if none selected */
2862: public int getCurrentRegionIndex() {
2863: if (_current != null)
2864: return getIndexOf(_current);
2865: return -1;
2866: }
2867:
2868: /** @return true if the current region is the first in the list, i.e. prevCurrentRegion is without effect */
2869: public boolean isCurrentRegionFirst() {
2870: return getIndexOf(_current) == 0;
2871: }
2872:
2873: /** @return true if the current region is the last in the list, i.e. nextCurrentRegion is without effect */
2874: public boolean isCurrentRegionLast() {
2875: return (getIndexOf(_current) == _regions.size() - 1);
2876: }
2877:
2878: /** Set the current region.
2879: * @param region new current region */
2880: public void setCurrentRegion(R region) {
2881: // if (!_regions.contains(_current)) _current = null;
2882: _current = region;
2883: }
2884:
2885: /** Make the region that is more recent the current region.
2886: * @return new current region */
2887: public R nextCurrentRegion() {
2888: if (_current != null) {
2889: int index = getIndexOf(_current);
2890: if (index + 1 < _regions.size())
2891: _current = _regions.get(index + 1);
2892: } else
2893: _current = _regions.lastElement();
2894: return _current;
2895: }
2896:
2897: /** Make the region that is less recent the current region.
2898: * @return new current region */
2899: public R prevCurrentRegion() {
2900: if (_current != null) {
2901: int index = getIndexOf(_current);
2902: if (index - 1 >= 0) {
2903: _current = _regions.get(index - 1);
2904: }
2905: } else {
2906: _current = _regions.lastElement();
2907: }
2908: return _current;
2909: }
2910:
2911: /** Set the maximum number of regions that can be stored in this manager. If the maximum capacity has been reached
2912: * and another region is added, the region at the end farther away from the insertion location will be discarded.
2913: * @param size maximum number of regions, or 0 if no maximum
2914: */
2915: public void setMaximumSize(int size) {
2916: _maxSize = size;
2917:
2918: // remove regions if necessary
2919: shrinkManager();
2920: }
2921:
2922: /** @return the maximum number of regions that can be stored in this manager. */
2923: public int getMaximumSize() {
2924: return _maxSize;
2925: }
2926:
2927: /** Apply the given command to the specified region to change it.
2928: * @param region the region to find and change
2929: * @param cmd command that mutates the region. */
2930: public void changeRegion(R region, Lambda<Object, R> cmd) {
2931: final int index = getIndexOf(region);
2932: if (index < 0) {
2933: return;
2934: }
2935: final R r = _regions.get(index);
2936: cmd.apply(r);
2937: Utilities.invokeLater(new Runnable() {
2938: public void run() {
2939: // notify
2940: _lock.startRead();
2941: try {
2942: for (RegionManagerListener<R> l : _listeners) {
2943: l.regionChanged(r, index);
2944: }
2945: } finally {
2946: _lock.endRead();
2947: }
2948: }
2949: });
2950: }
2951:
2952: /** Removes all listeners from this notifier. */
2953: public void removeAllListeners() {
2954: throw new UnsupportedOperationException(
2955: "ConcreteRegionManager does not support removing all listeners");
2956: // this would be a potentially dangerous thing to do, as it would also remove the listeners that the subsets
2957: // have installed
2958: }
2959: }
2960:
2961: /** Add the current location to the browser history. Aborts if not run in event thread. */
2962: public void addToBrowserHistory() {
2963: if (!EventQueue.isDispatchThread())
2964: return;
2965: final OpenDefinitionsDocument doc = getActiveDocument();
2966:
2967: int startPos = 0; // required by javac
2968: int endPos = 0; // required by javac
2969: File file = null; // required by javac
2970:
2971: if (doc != null) {
2972: try {
2973: startPos = doc.createPosition(doc.getCaretPosition())
2974: .getOffset();
2975: endPos = doc.createPosition(
2976: doc.getLineEndPos(doc.getCaretPosition()))
2977: .getOffset();
2978: file = doc.getFile();
2979: } catch (FileMovedException fme) { /* ignore */
2980: } catch (BadLocationException ble) {
2981: throw new UnexpectedException(ble);
2982: }
2983:
2984: getBrowserHistoryManager().addRegion(
2985: new SimpleDocumentRegion(doc, file, startPos,
2986: endPos));
2987: }
2988: }
2989:
2990: /** throws an UnsupportedOperationException */
2991: public Iterable<File> getClassPath() {
2992: throw new UnsupportedOperationException(
2993: "AbstractGlobalModel does not support class paths");
2994: }
2995:
2996: public static boolean isUntitled(final File f) {
2997: return f == null || (f instanceof NullFile);
2998: }
2999:
3000: // ---------- ConcreteOpenDefDoc inner class ----------
3001:
3002: /** A wrapper around a DefinitionsDocument or potential DefinitionsDocument (if it has been kicked out of the cache)
3003: * The GlobalModel interacts with DefinitionsDocuments through this wrapper.<br>
3004: * This call was formerly called the <code>DefinitionsDocumentHandler</code> but was renamed (2004-Jun-8) to be more
3005: * descriptive/intuitive. (Really? CC)
3006: */
3007: class ConcreteOpenDefDoc implements OpenDefinitionsDocument {
3008: protected class SubsetRegionManager<R extends DocumentRegion>
3009: extends EventNotifier<RegionManagerListener<R>>
3010: implements RegionManager<R> {
3011:
3012: /** The region manager it is a subset of. */
3013: private volatile RegionManager<R> _super SetManager;
3014:
3015: /** Creates a subset region manager that only sees the regions in this document. */
3016: public SubsetRegionManager(RegionManager<R> ssm) {
3017: _super SetManager = ssm;
3018: }
3019:
3020: /** @returns the superset manager. */
3021: public RegionManager<R> getSuperSetManager() {
3022: return _super SetManager;
3023: }
3024:
3025: /** Returns the region in this manager at the given offset, or null if one does not exist.
3026: * @param odd the document
3027: * @param offset the offset in the document
3028: * @return the DocumentRegion at the given line number, or null if it does not exist.
3029: */
3030: public R getRegionAt(OpenDefinitionsDocument odd, int offset) {
3031: return _super SetManager.getRegionAt(odd, offset);
3032: }
3033:
3034: /** Get the DocumentRegion that is stored in this RegionsTreePanel overlapping the area for the given document,
3035: * or null if it doesn't exist.
3036: * @param odd the document
3037: * @param startOffset the start offset
3038: * @param endOffset the end offset
3039: * @return the DocumentRegion or null
3040: */
3041: public R getRegionOverlapping(OpenDefinitionsDocument odd,
3042: int startOffset, int endOffset) {
3043: return _super SetManager.getRegionOverlapping(odd,
3044: startOffset, endOffset);
3045: }
3046:
3047: /** Add the supplied DocumentRegion to the manager.
3048: * @param region the DocumentRegion to be inserted into the manager
3049: */
3050: public void addRegion(R region) {
3051: _super SetManager.addRegion(region);
3052: }
3053:
3054: /** Remove the given DocumentRegion from the manager.
3055: * @param region the DocumentRegion to be removed.
3056: */
3057: public void removeRegion(R region) {
3058: _super SetManager.removeRegion(region);
3059: }
3060:
3061: /** @return a Vector<R> containing the DocumentRegion objects corresponding ONLY to this document. */
3062: public Vector<R> getRegions() {
3063: Vector<R> accum = new Vector<R>();
3064: Vector<R> regions = _super SetManager.getRegions();
3065: for (R r : regions) {
3066: if (r.getDocument().equals(ConcreteOpenDefDoc.this )) {
3067: accum.add(r);
3068: }
3069: }
3070: return accum;
3071: }
3072:
3073: /** Tells the manager to remove all regions corresponding ONLY to this document. */
3074: public void clearRegions() {
3075: Vector<R> regions = getRegions();
3076: for (R r : regions) {
3077: _super SetManager.removeRegion(r);
3078: }
3079: }
3080:
3081: /** Apply the given command to the specified region to change it.
3082: * @param region the region to find and change
3083: * @param cmd command that mutates the region. */
3084: public void changeRegion(R region, Lambda<Object, R> cmd) {
3085: _super SetManager.changeRegion(region, cmd);
3086: }
3087:
3088: /** A decorator to a RegionManagerListener that filters out everything but regions belonging to this document. */
3089: private class FilteredRegionManagerListener<R extends DocumentRegion>
3090: implements RegionManagerListener<R> {
3091: private RegionManagerListener<R> _decoree;
3092:
3093: public FilteredRegionManagerListener(
3094: RegionManagerListener<R> d) {
3095: _decoree = d;
3096: }
3097:
3098: public RegionManagerListener<R> getDecoree() {
3099: return _decoree;
3100: }
3101:
3102: public void regionAdded(R r, int index) {
3103: if (r.getDocument().equals(ConcreteOpenDefDoc.this )) {
3104: _decoree.regionAdded(r, index);
3105: }
3106: }
3107:
3108: public void regionChanged(R r, int index) {
3109: if (r.getDocument().equals(ConcreteOpenDefDoc.this )) {
3110: _decoree.regionChanged(r, index);
3111: }
3112: }
3113:
3114: public void regionRemoved(R r) {
3115: if (r.getDocument().equals(ConcreteOpenDefDoc.this )) {
3116: _decoree.regionRemoved(r);
3117: }
3118: }
3119: }
3120:
3121: /** All filtered listeners that are listening to this subset. Accesses to this collection are protected by the
3122: * ReaderWriterLock. The collection must be synchronized, since multiple readers could access it at once.
3123: */
3124: protected final LinkedList<FilteredRegionManagerListener<R>> _filters = new LinkedList<FilteredRegionManagerListener<R>>();
3125:
3126: /** Provides synchronization primitives for solving the readers/writers problem. In EventNotifier, adding and
3127: * removing listeners are considered write operations, and all notifications are considered read operations. Multiple
3128: * reads can occur simultaneously, but only one write can occur at a time, and no reads can occur during a write.
3129: */
3130: protected final ReaderWriterLock _lock = new ReaderWriterLock();
3131:
3132: /** Adds a listener to the notifier.
3133: * @param listener a listener that reacts on events
3134: */
3135: public void addListener(RegionManagerListener<R> listener) {
3136: FilteredRegionManagerListener<R> filter = new FilteredRegionManagerListener<R>(
3137: listener);
3138: _lock.startWrite();
3139: try {
3140: _filters.add(filter);
3141: } finally {
3142: _lock.endWrite();
3143: _super SetManager.addListener(filter);
3144: }
3145: }
3146:
3147: /** Removes a listener from the notifier.
3148: * @param listener a listener that reacts on events
3149: */
3150: public void removeListener(RegionManagerListener<R> listener) {
3151: _lock.startWrite();
3152: try {
3153: for (FilteredRegionManagerListener<R> filter : _filters) {
3154: if (filter.getDecoree().equals(listener)) {
3155: _listeners.remove(filter);
3156: _super SetManager.removeListener(filter);
3157: }
3158: }
3159: } finally {
3160: _lock.endWrite();
3161: }
3162: }
3163:
3164: /** Removes all listeners from this notifier. */
3165: public void removeAllListeners() {
3166: _lock.startWrite();
3167: try {
3168: for (FilteredRegionManagerListener<R> filter : _filters) {
3169: _listeners.remove(filter);
3170: _super SetManager.removeListener(filter);
3171: }
3172: } finally {
3173: _lock.endWrite();
3174: }
3175: }
3176:
3177: /** @return the current region or null if none selected. */
3178: public R getCurrentRegion() {
3179: // TODO
3180: throw new UnsupportedOperationException(
3181: "SubsetRegionManager.getCurrentRegion not supported");
3182: }
3183:
3184: /** @return the index of the current region or -1 if none selected. */
3185: public int getCurrentRegionIndex() {
3186: // TODO
3187: throw new UnsupportedOperationException(
3188: "SubsetRegionManager.getCurrentRegionIndex not supported");
3189: }
3190:
3191: /** Set the current region.
3192: * @param region new current region */
3193: public void setCurrentRegion(R region) {
3194: // TODO
3195: throw new UnsupportedOperationException(
3196: "SubsetRegionManager.setCurrentRegion not supported");
3197: }
3198:
3199: /** Make the region that is more recent the current region.
3200: * @return new current region */
3201: public R nextCurrentRegion() {
3202: // TODO
3203: throw new UnsupportedOperationException(
3204: "SubsetRegionManager.nextCurrentRegion not supported");
3205: }
3206:
3207: /** Make the region that is less recent the current region.
3208: * @return new current region */
3209: public R prevCurrentRegion() {
3210: // TODO
3211: throw new UnsupportedOperationException(
3212: "SubsetRegionManager.prevCurrentRegion not supported");
3213: }
3214:
3215: /** @return true if the current region is the first in the list, i.e. prevCurrentRegion is without effect */
3216: public boolean isCurrentRegionFirst() {
3217: // TODO
3218: throw new UnsupportedOperationException(
3219: "SubsetRegionManager.isCurrentRegionFirst not supported");
3220: }
3221:
3222: /** @return true if the current region is the last in the list, i.e. nextCurrentRegion is without effect */
3223: public boolean isCurrentRegionLast() {
3224: // TODO
3225: throw new UnsupportedOperationException(
3226: "SubsetRegionManager.isCurrentRegionLast not supported");
3227: }
3228:
3229: /** Sets the maximum number of regions that can be stored in this manager. If a region is added exceeding this
3230: * capacity, the region at the end farther away from the insertion location will be discarded.
3231: * @param size maximum number of regions, or 0 if no maximum
3232: */
3233: public void setMaximumSize(int size) {
3234: throw new UnsupportedOperationException(
3235: "SubsetRegionManager.setMaximumSize not supported");
3236: }
3237:
3238: /** @return the maximum number of regions that can be stored in this manager. */
3239: public int getMaximumSize() {
3240: throw new UnsupportedOperationException(
3241: "SubsetRegionManager.getMaximumSize not supported");
3242: }
3243: }
3244:
3245: // private boolean _modifiedSinceSave;
3246:
3247: /** String image of document as last read from or written to disk; initially null */
3248: private volatile String _image;
3249: private volatile File _file;
3250: private volatile long _timestamp;
3251:
3252: /** Caret position, as set by the view. (What does this mean? Only updated explicitly by setCurrentLocation(...) */
3253: private volatile int _caretPosition;
3254:
3255: /** The folder containing this document */
3256: private volatile File _parentDir;
3257:
3258: /** The cached class file for the document */
3259: private volatile File _classFile;
3260:
3261: /** Specifies if classFile is in sync with current state of the document */
3262: private volatile boolean _classFileInSync = false;
3263:
3264: /** The package name embedded in the document the last time is was loaded, reconstructed, or saved. When loading a
3265: * project, this information is extracted from the project file eliminating the need to read every document file.
3266: * For non-project files, it is extracted from the text of the file. If there is an error, it is left as "".
3267: */
3268: protected volatile String _packageName = "";
3269:
3270: private volatile DCacheAdapter _cacheAdapter;
3271:
3272: /** Manager for bookmark regions. */
3273: protected final SubsetRegionManager<Breakpoint> _breakpointManager;
3274:
3275: /** Manager for bookmark regions. */
3276: protected final SubsetRegionManager<DocumentRegion> _bookmarkManager;
3277:
3278: /** Manager for find result regions. */
3279: protected final LinkedList<SubsetRegionManager<MovingDocumentRegion>> _findResultsManagers;
3280:
3281: /** Manager for browser history regions. */
3282: protected final SubsetRegionManager<DocumentRegion> _browserHistoryManager;
3283:
3284: private volatile int _initVScroll;
3285: private volatile int _initHScroll;
3286: private volatile int _initSelStart;
3287: private volatile int _initSelEnd;
3288:
3289: private volatile int _id;
3290: private volatile DrJavaBook _book;
3291:
3292: /** Standard constructor for a document read from a file. Initializes this ODD's DD. Assumes that f exists.
3293: * @param f file describing DefinitionsDocument to manage; should be in canonical form
3294: */
3295: ConcreteOpenDefDoc(File f) {
3296: this (f, f.getParentFile(), f.lastModified());
3297: }
3298:
3299: /* Standard constructor for a new document (associated file is NullFile which does not exit in file system). */
3300: ConcreteOpenDefDoc(NullFile f) {
3301: this (f, null, 0L);
3302: }
3303:
3304: /* General constructor. Only used privately. */
3305: private ConcreteOpenDefDoc(File f, File dir, long stamp) {
3306:
3307: _file = f;
3308: _parentDir = dir;
3309: _classFile = null;
3310: _timestamp = stamp;
3311: _image = null;
3312: _id = ID_COUNTER++;
3313:
3314: try {
3315: DDReconstructor ddr = makeReconstructor();
3316: // System.err.println("Registering " + this);
3317: _cacheAdapter = _cache.register(this , ddr);
3318: } catch (IllegalStateException e) {
3319: throw new UnexpectedException(e);
3320: }
3321:
3322: _breakpointManager = new SubsetRegionManager<Breakpoint>(
3323: AbstractGlobalModel.this .getBreakpointManager());
3324: _bookmarkManager = new SubsetRegionManager<DocumentRegion>(
3325: AbstractGlobalModel.this .getBookmarkManager());
3326: _findResultsManagers = new LinkedList<SubsetRegionManager<MovingDocumentRegion>>();
3327: for (RegionManager<MovingDocumentRegion> rm : AbstractGlobalModel.this
3328: .getFindResultsManagers()) {
3329: addFindResultsManager(rm);
3330: }
3331: _browserHistoryManager = new SubsetRegionManager<DocumentRegion>(
3332: AbstractGlobalModel.this .getBrowserHistoryManager());
3333: }
3334:
3335: //------------ Getters and Setters -------------//
3336:
3337: /** Returns the file field for this document; does not check whether the file is NullFile or file exists. */
3338: public File getRawFile() {
3339: return _file;
3340: }
3341:
3342: /** Returns the file for this document, null if the document is null (which should never happen). If the document's
3343: * file does not exist, this throws a FileMovedException. If a FileMovedException is thrown, you
3344: * can retrieve the non-existence source file from the FileMovedException by using the getFile() method.
3345: * @return the file for this document
3346: */
3347: public File getFile() throws FileMovedException {
3348: File f = _file; // single read of f
3349: if (AbstractGlobalModel.isUntitled(f))
3350: return null; // assert f != null
3351: if (f.exists())
3352: return f;
3353: else
3354: throw new FileMovedException(f,
3355: "This document's file has been moved or deleted.");
3356: }
3357:
3358: /** Sets the file for this openDefinitionsDocument. */
3359: public void setFile(final File file) {
3360: synchronized (this ) { // ensures that _file and _timestamp are consistent
3361: _file = file;
3362: if (!AbstractGlobalModel.isUntitled(file))
3363: _timestamp = file.lastModified();
3364: else
3365: _timestamp = 0L;
3366: }
3367: }
3368:
3369: /** Returns the timestamp. */
3370: public long getTimestamp() {
3371: return _timestamp;
3372: }
3373:
3374: public void setClassFileInSync(boolean inSync) {
3375: _classFileInSync = inSync;
3376: }
3377:
3378: public boolean getClassFileInSync() {
3379: return _classFileInSync;
3380: }
3381:
3382: public void setCachedClassFile(File classFile) {
3383: _classFile = classFile;
3384: }
3385:
3386: public File getCachedClassFile() {
3387: return _classFile;
3388: }
3389:
3390: /** Whenever this document has been saved, this method should be called to update its "isModified" information. */
3391: public void resetModification() {
3392: synchronized (this ) {
3393: getDocument().resetModification();
3394: File f = _file;
3395: if (!AbstractGlobalModel.isUntitled(f))
3396: _timestamp = f.lastModified();
3397: }
3398: }
3399:
3400: /** @return The parent directory; should be in canonical form. */
3401: public File getParentDirectory() {
3402: return _parentDir;
3403: }
3404:
3405: /** Sets the parent directory of the document only if it is "Untitled"
3406: * @param pd The parent directory
3407: */
3408: public void setParentDirectory(File pd) {
3409: synchronized (this ) {
3410: if (!AbstractGlobalModel.isUntitled(_file))
3411: throw new IllegalArgumentException(
3412: "The parent directory can only be set for untitled documents");
3413: _parentDir = pd;
3414: }
3415: }
3416:
3417: public int getInitialVerticalScroll() {
3418: return _initVScroll;
3419: }
3420:
3421: public int getInitialHorizontalScroll() {
3422: return _initHScroll;
3423: }
3424:
3425: public int getInitialSelectionStart() {
3426: return _initSelStart;
3427: }
3428:
3429: public int getInitialSelectionEnd() {
3430: return _initSelEnd;
3431: }
3432:
3433: void setInitialVScroll(int i) {
3434: _initVScroll = i;
3435: }
3436:
3437: void setInitialHScroll(int i) {
3438: _initHScroll = i;
3439: }
3440:
3441: void setInitialSelStart(int i) {
3442: _initSelStart = i;
3443: }
3444:
3445: void setInitialSelEnd(int i) {
3446: _initSelEnd = i;
3447: }
3448:
3449: /** Gets the definitions document being handled.
3450: * @return document being handled
3451: */
3452: protected DefinitionsDocument getDocument() {
3453:
3454: // System.err.println("getDocument() called on " + this);
3455: try {
3456: return _cacheAdapter.getDocument();
3457: } catch (IOException ioe) { // document has been moved or deleted
3458: // Utilities.showDebug("getDocument() failed for " + this);
3459: try {
3460: _notifier.documentNotFound(this , _file);
3461: final String path = fixPathForNavigator(getFile()
3462: .getCanonicalFile().getCanonicalPath());
3463: Utilities.invokeLater(new SRunnable() { // formerly invokeAndWait(...) Why?
3464: public void run() {
3465: _documentNavigator.refreshDocument(
3466: ConcreteOpenDefDoc.this ,
3467: path);
3468: }
3469: });
3470: return _cacheAdapter.getDocument();
3471: } catch (Throwable t) {
3472: throw new UnexpectedException(t);
3473: }
3474: }
3475: }
3476:
3477: // /** Reconstructs the embedded positions for this document. */
3478: // public void makePositions() { _cacheAdapter.makePositions(); }
3479:
3480: /** Returns the name of the top level class, if any.
3481: * @throws ClassNameNotFoundException if no top level class name found.
3482: */
3483: public String getFirstTopLevelClassName()
3484: throws ClassNameNotFoundException {
3485: return getDocument().getFirstTopLevelClassName();
3486: }
3487:
3488: /** Returns the name of the main (public) class, if any.
3489: * @throws ClassNameNotFoundException if no top level class name found.
3490: */
3491: public String getMainClassName()
3492: throws ClassNameNotFoundException {
3493: return getDocument().getMainClassName();
3494: }
3495:
3496: /** Returns the name of this file, or "(Untitled)" if no file. */
3497: public String getFileName() {
3498: if (_file == null)
3499: return "(Untitled)";
3500: // if (isUntitled()) return "(Untitled)";
3501: return _file.getName();
3502: }
3503:
3504: /** Returns the name of the file for this document with an appended asterisk (if modified) or spaces */
3505: public String getName() {
3506: String fileName = getFileName();
3507: if (isModifiedSinceSave())
3508: fileName = fileName + "*";
3509: else
3510: fileName = fileName + " "; // forces the cell renderer to allocate space for an appended "*"
3511: return fileName;
3512: }
3513:
3514: /** Returns the canonical path for this document, "(Untitled)" if unsaved), "" if the file path is ill-formed. */
3515: public String getCanonicalPath() {
3516: if (isUntitled()) {
3517: return "(Untitled)";
3518: } else {
3519: return IOUtil.attemptCanonicalFile(getRawFile())
3520: .getPath();
3521: }
3522: }
3523:
3524: /** Returns the canonical path augmented by " *" if the document has been modified. */
3525: public String getCompletePath() {
3526: String path = getCanonicalPath();
3527: // Mark if modified
3528: if (isModifiedSinceSave())
3529: path = path + " *";
3530: return path;
3531: }
3532:
3533: /** Finds the root directory for the source file for this document; null if document is Untitled.
3534: * @return The root directory of the source files, based on the package statement.
3535: * @throws InvalidPackageException if the package statement is invalid,
3536: * or if it does not match up with the location of the source file.
3537: */
3538: public File getSourceRoot() throws InvalidPackageException {
3539: if (isUntitled())
3540: throw new InvalidPackageException(-1,
3541: "Can not get source root for unsaved file. Please save.");
3542:
3543: try {
3544: String[] packages = _packageName.split("\\.");
3545: if (packages.length == 1 && packages[0].equals("")) {
3546: packages = new String[0]; // split should do this, but it doesn't
3547: }
3548: File dir = getFile().getParentFile();
3549: for (String p : IterUtil.reverse(IterUtil
3550: .asIterable(packages))) {
3551: if (dir == null || !dir.getName().equals(p)) {
3552: String m = "File is in the wrong directory or is declared part of the wrong package. "
3553: + "Directory name "
3554: + ((dir == null) ? "(root)" : "'"
3555: + dir.getName() + "'")
3556: + " does not match package name '"
3557: + p
3558: + "'.";
3559: throw new InvalidPackageException(-1, m);
3560: }
3561: dir = dir.getParentFile();
3562: }
3563: if (dir == null) {
3564: // should not happen in typical cases -- requires the first package name to match the root's name,
3565: // which is usually not a valid identifier (like "" or "C:")
3566: throw new InvalidPackageException(-1,
3567: "File is in a directory tree with a null root");
3568: }
3569: return dir;
3570: } catch (FileMovedException fme) {
3571: throw new InvalidPackageException(-1,
3572: "File has been moved or deleted from its previous location. Please save.");
3573: }
3574: }
3575:
3576: /** @return the name of the package at the time of the most recent save or load operation. */
3577: public String getPackageName() {
3578: return _packageName;
3579: }
3580:
3581: /** Sets the cached _packageName for the preceding method. */
3582: public void setPackage(String name) {
3583: _packageName = name;
3584: }
3585:
3586: /** @return the name of the package currently embedded in document. */
3587: public String getPackageNameFromDocument() {
3588: return getDocument().getPackageName();
3589: }
3590:
3591: /** Originally designed to allow undoManager to set the current document to be modified whenever an undo
3592: * or redo is performed. Now it actually does this.
3593: */
3594: public void updateModifiedSinceSave() {
3595: getDocument().updateModifiedSinceSave();
3596: }
3597:
3598: /** Getter for document id; used to sort documents into creation order */
3599: public int id() {
3600: return _id;
3601: }
3602:
3603: /** Returns the Pageable object for printing.
3604: * @return A Pageable representing this document.
3605: */
3606: public Pageable getPageable() throws IllegalStateException {
3607: return _book;
3608: }
3609:
3610: /** Clears the pageable object used to hold the print job. */
3611: public void cleanUpPrintJob() {
3612: _book = null;
3613: }
3614:
3615: //--------------- Simple Predicates ---------------//
3616:
3617: /** A file is in the project if the source root is the same as the
3618: * project root. this means that project files must be saved at the
3619: * source root. (we query the model through the model's state)
3620: */
3621: public boolean inProjectPath() {
3622: return _state.inProjectPath(this );
3623: }
3624:
3625: /** An open file is in the new project if the source root is the same as the new project root. */
3626: public boolean inNewProjectPath(File projRoot) {
3627: try {
3628: return !isUntitled()
3629: && IOUtil.isMember(getFile(), projRoot);
3630: } catch (FileMovedException e) {
3631: return false;
3632: }
3633: }
3634:
3635: /** A file is in the project if it is explicitly listed as part of the project. */
3636: public boolean inProject() {
3637: return !isUntitled() && _state.inProject(_file);
3638: }
3639:
3640: /** Determines if the document is empty. */
3641: public boolean isEmpty() {
3642: return getLength() == 0;
3643: }
3644:
3645: /** @return true if this is an auxiliary file. */
3646: public boolean isAuxiliaryFile() {
3647: return !isUntitled() && _state.isAuxiliaryFile(_file);
3648: }
3649:
3650: /** @return true if this has a legal source file name (ends in extension ".java", ".dj0", ".dj1", or ".dj2". */
3651: public boolean isSourceFile() {
3652: if (isUntitled())
3653: return false; // assert _file != null
3654: String name = _file.getName();
3655: for (String ext : CompilerModel.EXTENSIONS) {
3656: if (name.endsWith(ext))
3657: return true;
3658: }
3659: return false;
3660: }
3661:
3662: /** Returns whether this document is currently untitled (indicating whether it has a file yet or not).
3663: * @return true if the document is untitled and has no file
3664: */
3665: public boolean isUntitled() {
3666: return AbstractGlobalModel.isUntitled(_file);
3667: }
3668:
3669: public boolean isUntitledAndEmpty() {
3670: return isUntitled() && getLength() == 0;
3671: } // should be synchronized?
3672:
3673: /** Returns true if the file exists on disk. Returns false if the file has been moved or deleted */
3674: public boolean fileExists() {
3675: File f = _file; // single read of _file;
3676: return !AbstractGlobalModel.isUntitled(f) && f.exists();
3677: }
3678:
3679: //--------------- Major Operations ----------------//
3680:
3681: /** Returns true if the file exists on disk. Prompts the user otherwise */
3682: public boolean verifyExists() {
3683: // Utilities.showDebug("verifyExists called on " + _file);
3684: if (fileExists())
3685: return true;
3686: //prompt the user to find it
3687: try {
3688: _notifier.documentNotFound(this , _file);
3689: File f = getFile();
3690: if (isUntitled())
3691: return false;
3692: String path = fixPathForNavigator(getFile()
3693: .getCanonicalPath());
3694: _documentNavigator.refreshDocument(this , path);
3695: return true;
3696: } catch (Exception e) {
3697: return false;
3698: }
3699: // catch(DocumentFileClosed e) { /* not clear what to do here */ }
3700: }
3701:
3702: /** Makes a default DDReconstructor that will make the corresponding DefinitionsDocument. */
3703: protected DDReconstructor makeReconstructor() {
3704: return new DDReconstructor() {
3705:
3706: // Brand New documents start at location 0
3707: private volatile int _loc = 0;
3708:
3709: // Start out with empty lists of listeners on the very first time the document is made
3710: private volatile DocumentListener[] _list = {};
3711: private volatile List<FinalizationListener<DefinitionsDocument>> _finalListeners = new LinkedList<FinalizationListener<DefinitionsDocument>>();
3712:
3713: // Weak hashmap that associates a WrappedPosition with its offset when saveDocInfo was called
3714: private volatile WeakHashMap<DefinitionsDocument.WrappedPosition, Integer> _positions = new WeakHashMap<DefinitionsDocument.WrappedPosition, Integer>();
3715:
3716: public String getText() {
3717: String image = _image;
3718: if (image != null)
3719: return image;
3720:
3721: // Document has not yet been read from disk; read it and set _image before returning text.
3722: // Synchronization on this was eliminated because it does not prevent the returned string from becoming
3723: // inconsistent with _doc/_file in the presence of huge scheduling delays. Of course, all getText operations
3724: // can return stale data in the presence of such delays.
3725: try {
3726: image = FileOps.readFileAsSwingText(_file);
3727: } catch (IOException e) {
3728: image = "";
3729: }
3730: // System.err.println("Returning image '" + image + " for file " + _file);
3731: _image = image;
3732: return _image;
3733: }
3734:
3735: public DefinitionsDocument make() throws IOException,
3736: BadLocationException, FileMovedException {
3737:
3738: // System.err.println("DDReconstructor.make() called on " + ConcreteOpenDefDoc.this);
3739: DefinitionsDocument newDefDoc = new DefinitionsDocument(
3740: _notifier);
3741: newDefDoc.setOpenDefDoc(ConcreteOpenDefDoc.this );
3742:
3743: /* Initialize doc contents */
3744: String image = getText();
3745: //if (image.length() > 0) newDefDoc.insertString(0, image, null); // Do not call insertString on an empty doc
3746:
3747: // else if (! isUntitled()) {
3748: // final InputStreamReader reader = new FileReader(_file);
3749: // _editorKit.read(reader, newDefDoc, 0);
3750: // reader.close(); // win32 needs readers closed explicitly!
3751: // }
3752:
3753: if (image != null) {
3754: _editorKit.read(new StringReader(image),
3755: newDefDoc, 0);
3756: // Set document property to write out document using newLine conventions of the host platform.
3757: newDefDoc
3758: .putProperty(
3759: DefaultEditorKit.EndOfLineStringProperty,
3760: StringOps.EOL);
3761: _log.log("Reading from image for " + _file
3762: + " containing " + _image.length()
3763: + " chars");
3764: }
3765:
3766: _loc = Math.min(_loc, image.length()); // make sure not past end
3767: _loc = Math.max(_loc, 0); // make sure not less than 0
3768: newDefDoc.setCurrentLocation(_loc);
3769: for (DocumentListener d : _list) {
3770: if (d instanceof DocumentUIListener)
3771: newDefDoc.addDocumentListener(d);
3772: }
3773: for (FinalizationListener<DefinitionsDocument> l : _finalListeners) {
3774: newDefDoc.addFinalizationListener(l);
3775: }
3776:
3777: // re-create and update all positions
3778: newDefDoc.setWrappedPositionOffsets(_positions);
3779:
3780: newDefDoc.resetModification(); // Why is this necessary? A reconstructed document is already unmodified.
3781:
3782: // tempDoc.setUndoManager(_undo);
3783: assert !newDefDoc.isModifiedSinceSave();
3784: // System.err.println ("_packageName in make() = " + _packageName);
3785: // System.err.println("tempDoc.getLength() = " + tempDoc.getLength());
3786: _packageName = newDefDoc.getPackageName();
3787: // System.err.println("make() returned " + newDefDoc);
3788: return newDefDoc;
3789: }
3790:
3791: // private volatile boolean _positionsMade = false;
3792: //
3793: // /** Reconstructs this document except for embedded positions. Assumes _cacheLock is held.
3794: // * @param image the bytes in the cached file image for this document.
3795: // */
3796: // public DefinitionsDocument make() throws IOException, BadLocationException, FileMovedException { // should sync on _file
3797: //
3798: //// System.err.println("DDReconstructor.make() called on " + ConcreteOpenDefDoc.this);
3799: // DefinitionsDocument newDefDoc = new DefinitionsDocument(_notifier);
3800: // newDefDoc.setOpenDefDoc(ConcreteOpenDefDoc.this);
3801: //
3802: // if (_image != null) {
3803: // _editorKit.read(new InputStreamReader(new ByteArrayInputStream(_image)), newDefDoc, 0);
3804: // _log.log("Reading from image for " + _file + " containing " + _image.length + " chars");
3805: // }
3806: // else if (! isUntitled()) {
3807: // final InputStreamReader reader = new FileReader(_file);
3808: // _editorKit.read(reader, newDefDoc, 0);
3809: // reader.close(); // win32 needs readers closed explicitly!
3810: // }
3811: // _loc = Math.min(_loc, newDefDoc.getLength()); // make sure not past end
3812: // _loc = Math.max(_loc, 0); // make sure not less than 0
3813: // newDefDoc.setCurrentLocation(_loc);
3814: // for (DocumentListener d : _list) {
3815: // if (d instanceof DocumentUIListener) newDefDoc.addDocumentListener(d);
3816: // }
3817: // for (FinalizationListener<DefinitionsDocument> l: _finalListeners) {
3818: // newDefDoc.addFinalizationListener(l);
3819: // }
3820: //
3821: // _positionsMade = false;
3822: //
3823: // newDefDoc.resetModification(); // Why is this necessary? A reconstructed document is already unmodified.
3824: //
3825: // // tempDoc.setUndoManager(_undo);
3826: // assert ! newDefDoc.isModifiedSinceSave();
3827: //// System.err.println ("_packageName in make() = " + _packageName);
3828: //// System.err.println("tempDoc.getLength() = " + tempDoc.getLength());
3829: // _packageName = newDefDoc.getPackageName();
3830: //// System.err.println("make() returned " + newDefDoc);
3831: // return newDefDoc;
3832: //
3833: // }
3834:
3835: // /** Reconstructs the embedded positions for this document. Synchronized*/
3836: // public void makePositions() {
3837: // if (_positionsMade) return;
3838: // synchronized(this) {
3839: // if (_positionsMade) return; // double-check works for volatile fields in Java 1.4 and later code
3840: // _positionsMade = true;
3841: // }
3842: // try { getDocument().setWrappedPositionOffsets(_positions); }
3843: // catch(Exception e) { /* ignore */ } // omitted positions are not fatal
3844: // }
3845:
3846: /** Saves the information for this document before it is kicked out of the cache. Only called from
3847: * DocumentCache. Assumes that cache lock is already held.
3848: */
3849: public void saveDocInfo(DefinitionsDocument doc) {
3850: // These lines were commented out to fix a memory leak; evidently, the undomanager holds on to the document
3851: // _undo = doc.getUndoManager();
3852: // _undoListeners = doc.getUndoableEditListeners();
3853: // Save document image. Note: this could be optimized to eliminate redundant updates to _image
3854: String text = doc.getText();
3855: if (text.length() > 0) {
3856: _image = text;
3857: // _log.log("Saving image containing " + _image.length() + " chars for " + _file);
3858: }
3859: _loc = doc.getCurrentLocation();
3860: _list = doc.getDocumentListeners();
3861: _finalListeners = doc.getFinalizationListeners();
3862:
3863: // save offsets of all positions
3864: _positions.clear();
3865: _positions = doc.getWrappedPositionOffsets();
3866: }
3867:
3868: public void addDocumentListener(DocumentListener dl) {
3869: ArrayList<DocumentListener> tmp = new ArrayList<DocumentListener>();
3870: for (DocumentListener l : _list) {
3871: if (dl != l)
3872: tmp.add(l);
3873: }
3874: tmp.add(dl);
3875: _list = tmp
3876: .toArray(new DocumentListener[tmp.size()]);
3877: }
3878:
3879: public String toString() {
3880: return ConcreteOpenDefDoc.this .toString();
3881: }
3882: };
3883: }
3884:
3885: /** Saves the document with a FileWriter. If the file name is already set, the method will use
3886: * that name instead of whatever selector is passed in.
3887: * @param com a selector that picks the file name if the doc is untitled
3888: * @exception IOException
3889: * @return true if the file was saved, false if the operation was canceled
3890: */
3891: public boolean saveFile(FileSaveSelector com)
3892: throws IOException {
3893: // System.err.println("saveFile called on " + this);
3894: // Update value of _packageName since modification flag will be set to false
3895: if (!isModifiedSinceSave())
3896: return true;
3897: if (isUntitled())
3898: return saveFileAs(com);
3899:
3900: // Didn't need to save since file is named and unmodified; return true, since the save wasn't "canceled"
3901:
3902: // System.err.println("Saving file: " + getFile());
3903:
3904: // Update package name by parsing file
3905: _packageName = getDocument().getPackageName();
3906: FileSaveSelector realCommand = com;
3907: try {
3908: final File file = getFile();
3909: // System.err.println("file name for doc to be saved is: " + file);
3910: if (!isUntitled()) {
3911: realCommand = new TrivialFSS(file);
3912: // System.err.println("TrivialFSS set up");
3913: }
3914: } catch (FileMovedException fme) {
3915: // getFile() failed, prompt the user if a new one should be selected
3916: if (com.shouldSaveAfterFileMoved(this , fme.getFile()))
3917: realCommand = com;
3918: else
3919: return false;
3920: // User declines to save as a new file, so don't save
3921: }
3922: // System.err.println("Calling saveFileAs");
3923: return saveFileAs(realCommand);
3924: }
3925:
3926: /** Saves the document with a FileWriter. The FileSaveSelector will either provide a file name or prompt the
3927: * user for one. It is up to the caller to decide what needs to be done to choose a file to save to. Once
3928: * the file has been saved succssfully, this method fires fileSave(File). If the save fails for any
3929: * reason, the event is not fired. This is synchronized against the compiler model to prevent saving and
3930: * compiling at the same time- this used to freeze drjava.
3931: * @param com a selector that picks the file name.
3932: * @throws IOException if the save fails due to an IO error
3933: * @return true if the file was saved, false if the operation was canceled
3934: */
3935: public boolean saveFileAs(FileSaveSelector com)
3936: throws IOException {
3937: // Update _packageName since modifiedSinceSaved flag will be set to false
3938: _packageName = getDocument().getPackageName();
3939: try {
3940: final OpenDefinitionsDocument openDoc = this ;
3941: final File file = com.getFile().getCanonicalFile();
3942: _log.log("saveFileAs called on " + file);
3943: OpenDefinitionsDocument otherDoc = _getOpenDocument(file);
3944:
3945: // Check if file is already open in another document
3946: boolean openInOtherDoc = ((otherDoc != null) && (openDoc != otherDoc));
3947:
3948: // If the file is open in another document, abort if user does not confirm overwriting it
3949: if (openInOtherDoc) {
3950: boolean shouldOverwrite = com.warnFileOpen(file);
3951: if (!shouldOverwrite)
3952: return true; // operation not cancelled? Strange
3953: }
3954:
3955: if (!file.exists() || com.verifyOverwrite()) { // confirm that existing file can be overwritten
3956:
3957: // System.err.println("Writing file " + file);
3958:
3959: // Correct the case of the filename (in Windows)
3960: if (!file.getCanonicalFile().getName().equals(
3961: file.getName()))
3962: file.renameTo(file);
3963:
3964: // Check for # in the path of the file because if there
3965: // is one, then the file cannot be used in the Interactions Pane
3966: if (file.getAbsolutePath().indexOf("#") != -1)
3967: _notifier.filePathContainsPound();
3968:
3969: // have FileOps save the file
3970: // System.err.println("Calling FileOps.saveFile to save it");
3971: FileOps
3972: .saveFile(new FileOps.DefaultFileSaver(file) {
3973: /** Only runs in event thread so no read lock is necessary. */
3974: public void saveTo(OutputStream os)
3975: throws IOException {
3976: DefinitionsDocument dd = getDocument();
3977: try {
3978: _editorKit.write(os, dd, 0, dd
3979: .getLength());
3980: // Utilities.show ("Wrote file containing:\n" + doc.getText());
3981: } catch (BadLocationException docFailed) {
3982: throw new UnexpectedException(
3983: docFailed);
3984: }
3985: }
3986: });
3987: resetModification();
3988: synchronized (_documentsRepos) {
3989: File f = getRawFile();
3990: // OpenDefinitionsDocument d = _documentsRepos.get(f);
3991: // d == this except in some unit tests where documents are not entered in _documentsRepos
3992: // assert d == this;
3993: _documentsRepos.remove(f);
3994: _documentsRepos.put(file, this );
3995: }
3996: setFile(file);
3997:
3998: // this.getPackageName does not return "" if this is untitled and contains a legal package declaration
3999: // try {
4000: // // This calls getDocument().getPackageName() because this may be untitled and this.getPackageName()
4001: // // returns "" if it's untitled. Right here we are interested in parsing the DefinitionsDocument's text
4002: // _packageName = getDocument().getPackageName();
4003: // }
4004: // catch(InvalidPackageException e) { _packageName = null; }
4005: setCachedClassFile(null);
4006: checkIfClassFileInSync();
4007:
4008: // Utilities.showDebug("ready to fire fileSaved for " + this);
4009: _notifier.fileSaved(openDoc);
4010:
4011: // Make sure this file is on the appropriate classpaths (does nothing in AbstractGlobalModel)
4012: addDocToClassPath(this );
4013:
4014: /* update the navigator */
4015: _documentNavigator
4016: .refreshDocument(this ,
4017: fixPathForNavigator(file
4018: .getCanonicalPath()));
4019:
4020: /* set project changed flag */
4021: setProjectChanged(true);
4022: }
4023: return true;
4024: } catch (OperationCanceledException oce) {
4025: // Thrown by com.getFile() if the user cancels.
4026: // We don't save if this happens.
4027: return false;
4028: }
4029: }
4030:
4031: /** This method tells the document to prepare all the DrJavaBook and PagePrinter objects. */
4032: public void preparePrintJob() throws BadLocationException,
4033: FileMovedException {
4034: String fileName = "(Untitled)";
4035: File sourceFile = getFile(); // single read of _file
4036: if (!AbstractGlobalModel.isUntitled(sourceFile))
4037: fileName = sourceFile.getAbsolutePath();
4038:
4039: _book = new DrJavaBook(getDocument().getText(), fileName,
4040: _pageFormat);
4041: }
4042:
4043: /** Prints the given document by bringing up a "Print" window. */
4044: public void print() throws PrinterException,
4045: BadLocationException, FileMovedException {
4046: preparePrintJob();
4047: PrinterJob printJob = PrinterJob.getPrinterJob();
4048: printJob.setPageable(_book);
4049: if (printJob.printDialog())
4050: printJob.print();
4051: cleanUpPrintJob();
4052: }
4053:
4054: /** throws UnsupportedOperationException */
4055: public void startCompile() throws IOException {
4056: throw new UnsupportedOperationException(
4057: "AbstractGlobalModel does not support compilation");
4058: }
4059:
4060: /** throws UnsupportedOperationException */
4061: public void runMain() throws IOException,
4062: ClassNameNotFoundException {
4063: throw new UnsupportedOperationException(
4064: "AbstractGlobalModel does not support running");
4065: }
4066:
4067: /** throws UnsupportedOperationException */
4068: public void startJUnit() throws IOException,
4069: ClassNotFoundException {
4070: throw new UnsupportedOperationException(
4071: "AbstractGlobalModel does not support unit testing");
4072: }
4073:
4074: /** throws UnsupportedOperationException */
4075: public void generateJavadoc(FileSaveSelector saver)
4076: throws IOException {
4077: throw new UnsupportedOperationException(
4078: "AbstractGlobalModel does not support javadoc");
4079: }
4080:
4081: /** Returns true if this document is resident in memory. _cacheAdapter should be non-null. */
4082: public boolean isReady() {
4083: return _cacheAdapter != null && _cacheAdapter.isReady();
4084: }
4085:
4086: /** Determines if the document has been modified since the last save.
4087: * @return true if the document has been modified
4088: */
4089: public boolean isModifiedSinceSave() {
4090: /* If the document has not been registered or it is virtualized (only stored on disk), then we know that
4091: * it is not modified. This method can be called by debugging code (via getName() on a
4092: * ConcreteOpenDefDoc) before the document has been registered (_cacheAdapter == null). */
4093: if (isReady())
4094: return getDocument().isModifiedSinceSave();
4095: else
4096: return false;
4097: }
4098:
4099: public void documentSaved() {
4100: _cacheAdapter.documentSaved();
4101: }
4102:
4103: public void documentModified() {
4104: _cacheAdapter.documentModified();
4105: _classFileInSync = false;
4106: }
4107:
4108: public void documentReset() {
4109: _cacheAdapter.documentReset();
4110: }
4111:
4112: /** Determines if the file for this document has been modified since it was loaded.
4113: * @return true if the file has been modified
4114: */
4115: public boolean modifiedOnDisk() {
4116: boolean ret = false;
4117: final File f = _file; // single read of f
4118: if (!AbstractGlobalModel.isUntitled(f))
4119: ret = (f.lastModified() > _timestamp);
4120: return ret;
4121: }
4122:
4123: /** Determines if document has a class file consistent with its current state. If this document is unmodified,
4124: * this method examines the primary class file corresponding to this document and compares the timestamps of
4125: * the class file to that of the source file. An empty untitled document is consider to be "in sync".
4126: */
4127: public boolean checkIfClassFileInSync() {
4128: _log.log("checkIfClassFileInSync() called for " + this );
4129: if (isEmpty())
4130: return true;
4131:
4132: // If modified, then definitely out of sync
4133:
4134: if (isModifiedSinceSave()) {
4135: setClassFileInSync(false);
4136: // _log.log("checkIfClassFileInSync = false because isModifiedSinceSave()");
4137: return false;
4138: }
4139:
4140: // Look for cached class file
4141: File classFile = getCachedClassFile();
4142: // _log.log("In checkIfClassFileInSync cacched value of classFile = " + classFile);
4143: if (classFile == null) {
4144: // Not cached, so locate the file
4145: classFile = _locateClassFile();
4146: // _log.log(this + ": in checkIfClassFileInSync _locateClassFile() = " + classFile);
4147: setCachedClassFile(classFile);
4148: if ((classFile == null) || (!classFile.exists())) {
4149: // couldn't find the class file
4150: // _log.log(this + ": Could not find class file");
4151: setClassFileInSync(false);
4152: return false;
4153: }
4154: }
4155:
4156: // compare timestamps
4157:
4158: File sourceFile;
4159: try {
4160: sourceFile = getFile();
4161: } catch (FileMovedException fme) {
4162: setClassFileInSync(false);
4163: // _log.log(this + ": File moved");
4164: return false;
4165: }
4166: if (sourceFile != null) {
4167: _log.log(sourceFile + " has timestamp "
4168: + sourceFile.lastModified());
4169: _log.log(classFile + " has timestamp "
4170: + classFile.lastModified());
4171: }
4172: if (sourceFile == null
4173: || sourceFile.lastModified() > classFile
4174: .lastModified()) { // assert sourceFile != null
4175: setClassFileInSync(false);
4176: // _log.log(this + ": date stamps indicate modification");
4177: return false;
4178: } else {
4179: setClassFileInSync(true);
4180: return true;
4181: }
4182: }
4183:
4184: /** Returns the class file for this source document by searching the source roots of open documents, the
4185: * system classpath, and the "extra.classpath ". Returns null if the class file could not be found.
4186: */
4187: private File _locateClassFile() {
4188: // TODO: define in terms of GlobalModel.getClassPath()
4189:
4190: if (isUntitled())
4191: return null;
4192:
4193: String className;
4194: try {
4195: className = getDocument().getQualifiedClassName();
4196: } catch (ClassNameNotFoundException cnnfe) {
4197: _log
4198: .log("_locateClassFile() failed for "
4199: + this
4200: + " because getQualifedClassName returned ClassNotFound");
4201: return null; /* No source class name */
4202: }
4203: _log.log("In _locateClassFile, className = " + className);
4204: String ps = System.getProperty("file.separator");
4205: // replace periods with the System's file separator
4206: className = StringOps.replace(className, ".", ps);
4207: String fileName = className + ".class";
4208:
4209: _log
4210: .log("In _locateClassFile, classfileName = "
4211: + fileName);
4212:
4213: // Check source root set (open files)
4214: ArrayList<File> roots = new ArrayList<File>();
4215:
4216: if (getBuildDirectory() != null)
4217: roots.add(getBuildDirectory());
4218:
4219: // Add the current document to the beginning of the roots list
4220: try {
4221: File root = getSourceRoot();
4222: _log.log("Directory " + root
4223: + " added to list of source roots");
4224: roots.add(root);
4225: } catch (InvalidPackageException ipe) {
4226: try {
4227: _log
4228: .log(this
4229: + " has no source root, using parent directory instead");
4230: File root = getFile().getParentFile();
4231: if (root != null) {
4232: roots.add(root);
4233: _log.log("Added parent directory " + root
4234: + " to list of source roots");
4235: }
4236: } catch (NullPointerException e) {
4237: throw new UnexpectedException(e);
4238: } catch (FileMovedException fme) {
4239: // Moved, but we'll add the old file to the set anyway
4240: _log
4241: .log("File for "
4242: + this
4243: + "has moved; adding parent directory to list of roots");
4244: File root = fme.getFile().getParentFile();
4245: if (root != null)
4246: roots.add(root);
4247: }
4248: }
4249:
4250: File classFile = findFileInPaths(fileName, roots);
4251: if (classFile != null) {
4252: _log.log("Found source file " + classFile + " for "
4253: + this );
4254: return classFile;
4255: }
4256:
4257: _log.log(this + " not found on path of source roots");
4258: // Class not on source root set, check system classpath
4259: classFile = findFileInPaths(fileName, RUNTIME_CLASS_PATH);
4260:
4261: if (classFile != null)
4262: return classFile;
4263:
4264: // not on system classpath, check interactions classpath
4265: Vector<File> cpSetting = DrJava.getConfig().getSetting(
4266: EXTRA_CLASSPATH);
4267: return findFileInPaths(fileName, cpSetting);
4268: }
4269:
4270: /** Determines if the definitions document has been changed by an outside agent. If the document has changed,
4271: * asks the listeners if the GlobalModel should revert the document to the most recent version saved.
4272: * @return true if document has been reverted
4273: */
4274: public boolean revertIfModifiedOnDisk() throws IOException {
4275: final OpenDefinitionsDocument doc = this ;
4276: if (modifiedOnDisk()) {
4277: boolean shouldRevert = _notifier.shouldRevertFile(doc);
4278: if (shouldRevert)
4279: doc.revertFile();
4280: return shouldRevert;
4281: }
4282: return false;
4283: }
4284:
4285: /** Degenerate version of close; does not remove breakpoints in this document */
4286: public void close() {
4287: removeFromDebugger();
4288: _cacheAdapter.close();
4289: }
4290:
4291: /** Reverts current ODD to file content on disk. */
4292: public void revertFile() throws IOException {
4293:
4294: final OpenDefinitionsDocument doc = this ;
4295:
4296: //need to remove old, possibly invalid breakpoints
4297: removeFromDebugger();
4298: doc.getBreakpointManager().clearRegions();
4299: doc.getBookmarkManager().clearRegions();
4300: for (RegionManager<MovingDocumentRegion> rm : doc
4301: .getFindResultsManagers())
4302: rm.clearRegions();
4303: doc.getBrowserHistoryManager().clearRegions();
4304:
4305: if (doc.isUntitled())
4306: throw new UnexpectedException(
4307: "Cannot revert an Untitled file!");
4308:
4309: try {
4310: //this line precedes .remove() so that an invalid file is not cleared before this fact is discovered.
4311: File file = doc.getFile();
4312: FileReader reader = new FileReader(file);
4313: doc.clear();
4314:
4315: _editorKit.read(reader, doc, 0);
4316: reader.close(); // win32 needs readers closed explicitly!
4317:
4318: resetModification();
4319: doc.checkIfClassFileInSync();
4320: setCurrentLocation(0);
4321: _notifier.fileReverted(doc);
4322: } catch (BadLocationException e) {
4323: throw new UnexpectedException(e);
4324: }
4325: }
4326:
4327: /** Asks the listeners if the GlobalModel can abandon the current document. Fires the canAbandonFile(File)
4328: * event if isModifiedSinceSave() is true. Only executes in event thread except for tests.
4329: * @return true if the current document can be abandoned, false if the current action should be halted in
4330: * its tracks (e.g., file open when the document has been modified since the last save).
4331: */
4332: public boolean canAbandonFile() {
4333: // assert EventQueue.isDispatchThread();
4334: if (isUntitledAndEmpty())
4335: return true;
4336: File f = _file;
4337: if (isModifiedSinceSave()
4338: || (!AbstractGlobalModel.isUntitled(f)
4339: && !f.exists() && _cacheAdapter.isReady()))
4340: return _notifier.canAbandonFile(this );
4341: else
4342: return true;
4343: }
4344:
4345: /** Fires the quit(File) event if isModifiedSinceSave() is true. The quitFile() event asks the user if the
4346: * the file should be saved before quitting. Only executes in event thread.
4347: * @return true if quitting should continue, false if the user cancelled
4348: */
4349: public boolean quitFile() {
4350: assert EventQueue.isDispatchThread();
4351: File f = _file;
4352: if (isModifiedSinceSave()
4353: || (f != null && !f.exists() && _cacheAdapter
4354: .isReady()))
4355: return _notifier.quitFile(this );
4356: return true;
4357: }
4358:
4359: /** Moves the definitions document to the given line, and returns the resulting character position.
4360: * @param line Destination line number. If it exceeds the number of lines in the document, it is
4361: * interpreted as the last line.
4362: * @return Index into document of where it moved
4363: */
4364: public int gotoLine(int line) {
4365: DefinitionsDocument dd = getDocument();
4366: final int offset = getOffsetOfLine(line - 1);
4367: setCurrentLocation(offset);
4368: return offset;
4369: }
4370:
4371: /** Forwarding method to sync the definitions with whatever view component is representing them. */
4372: public void setCurrentLocation(int location) {
4373: _caretPosition = location;
4374: getDocument().setCurrentLocation(location);
4375: }
4376:
4377: /** Get the location of the cursor in the definitions according to the definitions document. */
4378: public int getCurrentLocation() {
4379: return getDocument().getCurrentLocation();
4380: }
4381:
4382: /** @return the caret position as set by the view. */
4383: public int getCaretPosition() {
4384: return _caretPosition;
4385: }
4386:
4387: /** Forwarding method to find the match for the closing brace immediately to the left, assuming there is such a brace.
4388: * @return the relative distance backwards to the offset before the matching brace.
4389: */
4390: public int balanceBackward() {
4391: return getDocument().balanceBackward();
4392: }
4393:
4394: /** Forwarding method to find the match for the open brace immediately to the right, assuming there is such a brace.
4395: * @return the relative distance forwards to the offset after `the matching brace.
4396: */
4397: public int balanceForward() {
4398: return getDocument().balanceForward();
4399: }
4400:
4401: /** @return the breakpoint region manager. */
4402: public RegionManager<Breakpoint> getBreakpointManager() {
4403: return _breakpointManager;
4404: }
4405:
4406: /** @return the bookmark region manager. */
4407: public RegionManager<DocumentRegion> getBookmarkManager() {
4408: return _bookmarkManager;
4409: }
4410:
4411: /** @return the find result region managers. */
4412: public List<RegionManager<MovingDocumentRegion>> getFindResultsManagers() {
4413: LinkedList<RegionManager<MovingDocumentRegion>> newList = new LinkedList<RegionManager<MovingDocumentRegion>>();
4414: for (SubsetRegionManager<MovingDocumentRegion> rm : _findResultsManagers) {
4415: newList.add(rm);
4416: }
4417: return newList;
4418: }
4419:
4420: /** Add a region manager for find results to this document.
4421: * @param rm the global model's region manager */
4422: public void addFindResultsManager(
4423: RegionManager<MovingDocumentRegion> rm) {
4424: _findResultsManagers
4425: .add(new SubsetRegionManager<MovingDocumentRegion>(
4426: rm));
4427: }
4428:
4429: /** Remove a manager for find results from this document.
4430: * @param rm the global model's region manager. */
4431: public void removeFindResultsManager(
4432: RegionManager<MovingDocumentRegion> rm) {
4433: for (SubsetRegionManager<MovingDocumentRegion> ssrm : _findResultsManagers) {
4434: if (ssrm.getSuperSetManager().equals(rm)) {
4435: _findResultsManagers.remove(ssrm);
4436: break;
4437: }
4438: }
4439: }
4440:
4441: /** @return manager for browser history regions for this document. */
4442: public RegionManager<DocumentRegion> getBrowserHistoryManager() {
4443: return _browserHistoryManager;
4444: }
4445:
4446: /** throws UnsupportedOperationException */
4447: public void removeFromDebugger() { /* do nothing because it is called in methods in this class */
4448: }
4449:
4450: public String toString() {
4451: return getFileName();
4452: }
4453:
4454: /** Orders ODDs by their id's. */
4455: public int compareTo(OpenDefinitionsDocument o) {
4456: return _id - o.id();
4457: }
4458:
4459: /** Implementation of the javax.swing.text.Document interface. */
4460: public void addDocumentListener(DocumentListener listener) {
4461: if (_cacheAdapter.isReady())
4462: getDocument().addDocumentListener(listener);
4463: else
4464: _cacheAdapter.addDocumentListener(listener);
4465: }
4466:
4467: List<UndoableEditListener> _undoableEditListeners = new LinkedList<UndoableEditListener>();
4468:
4469: public void addUndoableEditListener(
4470: UndoableEditListener listener) {
4471: _undoableEditListeners.add(listener);
4472: getDocument().addUndoableEditListener(listener);
4473: }
4474:
4475: public void removeUndoableEditListener(
4476: UndoableEditListener listener) {
4477: _undoableEditListeners.remove(listener);
4478: getDocument().removeUndoableEditListener(listener);
4479: }
4480:
4481: public UndoableEditListener[] getUndoableEditListeners() {
4482: return getDocument().getUndoableEditListeners();
4483: }
4484:
4485: public Position createUnwrappedPosition(int offs)
4486: throws BadLocationException {
4487: return getDocument().createUnwrappedPosition(offs);
4488: }
4489:
4490: public Position createPosition(int offs)
4491: throws BadLocationException {
4492: return getDocument().createPosition(offs);
4493: }
4494:
4495: public Element getDefaultRootElement() {
4496: return getDocument().getDefaultRootElement();
4497: }
4498:
4499: public Position getEndPosition() {
4500: return getDocument().getEndPosition();
4501: }
4502:
4503: public int getLength() {
4504: return _cacheAdapter.getLength();
4505: }
4506:
4507: public Object getProperty(Object key) {
4508: return getDocument().getProperty(key);
4509: }
4510:
4511: public Element[] getRootElements() {
4512: return getDocument().getRootElements();
4513: }
4514:
4515: public Position getStartPosition() {
4516: return getDocument().getStartPosition();
4517: }
4518:
4519: // public String getText() {
4520: // synchronized(_cache._cacheLock) { // lock down the cache
4521: // if (! _cacheAdapter.isReady() && _image != null) return _image;
4522: // }
4523: // return getDocumentText();
4524: // }
4525:
4526: // The following method must be renamed as private getDocumentText if the preceding code is commented in.
4527:
4528: /** Gets the text of this. Avoids reloading the document if it is kicked out of the cache. */
4529: public String getText() {
4530: return _cacheAdapter.getText();
4531: }
4532:
4533: /** Gets the specified substring of this. Avoids reloading the document if it is kicked out of the cache. */
4534: public String getText(int offset, int length)
4535: throws BadLocationException {
4536: return _cacheAdapter.getText(offset, length);
4537: }
4538:
4539: public void getText(int offset, int length, Segment txt)
4540: throws BadLocationException {
4541: getDocument().getText(offset, length, txt);
4542: }
4543:
4544: public void insertString(int offset, String str, AttributeSet a)
4545: throws BadLocationException {
4546: getDocument().insertString(offset, str, a);
4547: }
4548:
4549: public void append(String str, AttributeSet set) {
4550: getDocument().append(str, set);
4551: }
4552:
4553: public void append(String str, Style style) {
4554: getDocument().append(str, style);
4555: }
4556:
4557: public void putProperty(Object key, Object value) {
4558: getDocument().putProperty(key, value);
4559: }
4560:
4561: public void remove(int offs, int len)
4562: throws BadLocationException {
4563: getDocument().remove(offs, len);
4564: }
4565:
4566: public void removeDocumentListener(DocumentListener listener) {
4567: getDocument().removeDocumentListener(listener);
4568: }
4569:
4570: public void render(Runnable r) {
4571: getDocument().render(r);
4572: }
4573:
4574: /** End implementation of javax.swing.text.Document interface. */
4575:
4576: /** If the undo manager is unavailable, no undos are available
4577: * @return whether the undo manager can perform any undo's
4578: */
4579: public boolean undoManagerCanUndo() {
4580: return _cacheAdapter.isReady()
4581: && getUndoManager().canUndo();
4582: }
4583:
4584: /**
4585: * If the undo manager is unavailable, no redos are available
4586: * @return whether the undo manager can perform any redo's
4587: */
4588: public boolean undoManagerCanRedo() {
4589: return _cacheAdapter.isReady()
4590: && getUndoManager().canRedo();
4591: }
4592:
4593: /** Decorator pattern for the definitions document. */
4594: public CompoundUndoManager getUndoManager() {
4595: return getDocument().getUndoManager();
4596: }
4597:
4598: public int getLineStartPos(int pos) {
4599: return getDocument().getLineStartPos(pos);
4600: }
4601:
4602: public int getLineEndPos(int pos) {
4603: return getDocument().getLineEndPos(pos);
4604: }
4605:
4606: public int commentLines(int selStart, int selEnd) {
4607: return getDocument().commentLines(selStart, selEnd);
4608: }
4609:
4610: public int uncommentLines(int selStart, int selEnd) {
4611: return getDocument().uncommentLines(selStart, selEnd);
4612: }
4613:
4614: public void indentLines(int selStart, int selEnd) {
4615: getDocument().indentLines(selStart, selEnd);
4616: }
4617:
4618: public int getCurrentLine() {
4619: return getDocument().getCurrentLine();
4620: }
4621:
4622: public int getCurrentCol() {
4623: return getDocument().getCurrentCol();
4624: }
4625:
4626: public int getIntelligentBeginLinePos(int currPos)
4627: throws BadLocationException {
4628: return getDocument().getIntelligentBeginLinePos(currPos);
4629: }
4630:
4631: public int getOffset(int lineNum) {
4632: return getDocument().getOffset(lineNum);
4633: }
4634:
4635: public String getQualifiedClassName()
4636: throws ClassNameNotFoundException {
4637: return getDocument().getQualifiedClassName();
4638: }
4639:
4640: public String getQualifiedClassName(int pos)
4641: throws ClassNameNotFoundException {
4642: return getDocument().getQualifiedClassName(pos);
4643: }
4644:
4645: public ReducedModelState getStateAtCurrent() {
4646: return getDocument().getStateAtCurrent();
4647: }
4648:
4649: public void resetUndoManager() {
4650: // if it's not in the cache, the undo manager will be reset when it's reconstructed
4651: if (_cacheAdapter.isReady())
4652: getDocument().resetUndoManager();
4653: }
4654:
4655: public DocumentListener[] getDocumentListeners() {
4656: return getDocument().getDocumentListeners();
4657: }
4658:
4659: //--------- DJDocument methods ----------
4660:
4661: public void setTab(String tab, int pos) {
4662: getDocument().setTab(tab, pos);
4663: }
4664:
4665: public int getWhiteSpace() {
4666: return getDocument().getWhiteSpace();
4667: }
4668:
4669: public boolean posInParenPhrase(int pos) {
4670: return getDocument().posInParenPhrase(pos);
4671: }
4672:
4673: public boolean posInParenPhrase() {
4674: return getDocument().posInParenPhrase();
4675: }
4676:
4677: public String getEnclosingClassName(int pos,
4678: boolean fullyQualified) throws BadLocationException,
4679: ClassNameNotFoundException {
4680: return getDocument().getEnclosingClassName(pos,
4681: fullyQualified);
4682: }
4683:
4684: public int findPrevEnclosingBrace(int pos, char opening,
4685: char closing) throws BadLocationException {
4686: return getDocument().findPrevEnclosingBrace(pos, opening,
4687: closing);
4688: }
4689:
4690: public int findNextEnclosingBrace(int pos, char opening,
4691: char closing) throws BadLocationException {
4692: return getDocument().findNextEnclosingBrace(pos, opening,
4693: closing);
4694: }
4695:
4696: public int findPrevNonWSCharPos(int pos)
4697: throws BadLocationException {
4698: return getDocument().findPrevNonWSCharPos(pos);
4699: }
4700:
4701: public int getFirstNonWSCharPos(int pos)
4702: throws BadLocationException {
4703: return getDocument().getFirstNonWSCharPos(pos);
4704: }
4705:
4706: public int getFirstNonWSCharPos(int pos, boolean acceptComments)
4707: throws BadLocationException {
4708: return getDocument().getFirstNonWSCharPos(pos,
4709: acceptComments);
4710: }
4711:
4712: public int getFirstNonWSCharPos(int pos, char[] whitespace,
4713: boolean acceptComments) throws BadLocationException {
4714: return getDocument().getFirstNonWSCharPos(pos, whitespace,
4715: acceptComments);
4716: }
4717:
4718: public int getLineFirstCharPos(int pos)
4719: throws BadLocationException {
4720: return getDocument().getLineFirstCharPos(pos);
4721: }
4722:
4723: public int findCharOnLine(int pos, char findChar) {
4724: return getDocument().findCharOnLine(pos, findChar);
4725: }
4726:
4727: public String getIndentOfCurrStmt(int pos)
4728: throws BadLocationException {
4729: return getDocument().getIndentOfCurrStmt(pos);
4730: }
4731:
4732: public String getIndentOfCurrStmt(int pos, char[] delims)
4733: throws BadLocationException {
4734: return getDocument().getIndentOfCurrStmt(pos, delims);
4735: }
4736:
4737: public String getIndentOfCurrStmt(int pos, char[] delims,
4738: char[] whitespace) throws BadLocationException {
4739: return getDocument().getIndentOfCurrStmt(pos, delims,
4740: whitespace);
4741: }
4742:
4743: public void indentLines(int selStart, int selEnd,
4744: Indenter.IndentReason reason, ProgressMonitor pm)
4745: throws OperationCanceledException {
4746: getDocument().indentLines(selStart, selEnd, reason, pm);
4747: }
4748:
4749: public int findPrevCharPos(int pos, char[] whitespace)
4750: throws BadLocationException {
4751: return getDocument().findPrevCharPos(pos, whitespace);
4752: }
4753:
4754: public boolean findCharInStmtBeforePos(char findChar,
4755: int position) {
4756: return getDocument().findCharInStmtBeforePos(findChar,
4757: position);
4758: }
4759:
4760: public int findPrevDelimiter(int pos, char[] delims)
4761: throws BadLocationException {
4762: return getDocument().findPrevDelimiter(pos, delims);
4763: }
4764:
4765: public int findPrevDelimiter(int pos, char[] delims,
4766: boolean skipParenPhrases) throws BadLocationException {
4767: return getDocument().findPrevDelimiter(pos, delims,
4768: skipParenPhrases);
4769: }
4770:
4771: public void resetReducedModelLocation() {
4772: getDocument().resetReducedModelLocation();
4773: }
4774:
4775: public ReducedModelState stateAtRelLocation(int dist) {
4776: return getDocument().stateAtRelLocation(dist);
4777: }
4778:
4779: public IndentInfo getIndentInformation() {
4780: return getDocument().getIndentInformation();
4781: }
4782:
4783: public void move(int dist) {
4784: getDocument().move(dist);
4785: }
4786:
4787: public Vector<HighlightStatus> getHighlightStatus(int start,
4788: int end) {
4789: return getDocument().getHighlightStatus(start, end);
4790: }
4791:
4792: public void setIndent(int indent) {
4793: getDocument().setIndent(indent);
4794: }
4795:
4796: public int getIndent() {
4797: return getDocument().getIndent();
4798: }
4799:
4800: //-----------------------
4801:
4802: /** This method is put here because the ODD is the only way to get to the defdoc. */
4803: public void addFinalizationListener(
4804: FinalizationListener<DefinitionsDocument> fl) {
4805: getDocument().addFinalizationListener(fl);
4806: }
4807:
4808: public List<FinalizationListener<DefinitionsDocument>> getFinalizationListeners() {
4809: return getDocument().getFinalizationListeners();
4810: }
4811:
4812: // Styled Document Methods
4813: public Font getFont(AttributeSet attr) {
4814: return getDocument().getFont(attr);
4815: }
4816:
4817: public Color getBackground(AttributeSet attr) {
4818: return getDocument().getBackground(attr);
4819: }
4820:
4821: public Color getForeground(AttributeSet attr) {
4822: return getDocument().getForeground(attr);
4823: }
4824:
4825: public Element getCharacterElement(int pos) {
4826: return getDocument().getCharacterElement(pos);
4827: }
4828:
4829: public Element getParagraphElement(int pos) {
4830: return getDocument().getParagraphElement(pos);
4831: }
4832:
4833: public Style getLogicalStyle(int p) {
4834: return getDocument().getLogicalStyle(p);
4835: }
4836:
4837: public void setLogicalStyle(int pos, Style s) {
4838: getDocument().setLogicalStyle(pos, s);
4839: }
4840:
4841: public void setCharacterAttributes(int offset, int length,
4842: AttributeSet s, boolean replace) {
4843: getDocument().setCharacterAttributes(offset, length, s,
4844: replace);
4845: }
4846:
4847: public void setParagraphAttributes(int offset, int length,
4848: AttributeSet s, boolean replace) {
4849: getDocument().setParagraphAttributes(offset, length, s,
4850: replace);
4851: }
4852:
4853: public Style getStyle(String nm) {
4854: return getDocument().getStyle(nm);
4855: }
4856:
4857: public void removeStyle(String nm) {
4858: getDocument().removeStyle(nm);
4859: }
4860:
4861: public Style addStyle(String nm, Style parent) {
4862: return getDocument().addStyle(nm, parent);
4863: }
4864:
4865: public void clear() {
4866: getDocument().clear();
4867: }
4868:
4869: /* Locking operations in DJDocument interface */
4870:
4871: /** Swing-style readLock(). */
4872: public void acquireReadLock() {
4873: getDocument().acquireReadLock();
4874: }
4875:
4876: /** Swing-style readUlLock(). */
4877: public void releaseReadLock() {
4878: getDocument().releaseReadLock();
4879: }
4880:
4881: /** Swing-style writeLock(). */
4882: public void acquireWriteLock() {
4883: getDocument().acquireWriteLock();
4884: }
4885:
4886: /** Swing-style writeUnlock(). */
4887: public void releaseWriteLock() {
4888: getDocument().releaseWriteLock();
4889: }
4890:
4891: // public int getLockState() { return getDocument().getLockState(); }
4892:
4893: /** @return the number of lines in this document. */
4894: public int getNumberOfLines() {
4895: return getLineOfOffset(getLength());
4896: }
4897:
4898: /** Translates an offset into the components text to a line number.
4899: * @param offset the offset >= 0
4900: * @return the line number >= 0
4901: */
4902: public int getLineOfOffset(int offset) {
4903: return getDefaultRootElement().getElementIndex(offset);
4904: }
4905:
4906: /** Translates a line number into an offset.
4907: * @param line number >= 0
4908: * @return offset >= 0
4909: */
4910: public int getOffsetOfLine(int line) {
4911: final int count = getDefaultRootElement().getElementCount();
4912: if (line >= count) {
4913: line = count - 1;
4914: }
4915: return getDefaultRootElement().getElement(line)
4916: .getStartOffset();
4917: }
4918: } /* End of ConcreteOpenDefDoc */
4919:
4920: private static class TrivialFSS implements FileSaveSelector {
4921: private File _file;
4922:
4923: private TrivialFSS(File file) {
4924: _file = file;
4925: }
4926:
4927: public File getFile() throws OperationCanceledException {
4928: return _file;
4929: }
4930:
4931: public boolean warnFileOpen(File f) {
4932: return true;
4933: }
4934:
4935: public boolean verifyOverwrite() {
4936: return true;
4937: }
4938:
4939: public boolean shouldSaveAfterFileMoved(
4940: OpenDefinitionsDocument doc, File oldFile) {
4941: return true;
4942: }
4943: }
4944:
4945: /** Creates a ConcreteOpenDefDoc for a NullFile object f (corresponding to a new empty document)
4946: * @return OpenDefinitionsDocument object for a new document
4947: */
4948: protected ConcreteOpenDefDoc _createOpenDefinitionsDocument(
4949: NullFile f) {
4950: return new ConcreteOpenDefDoc(f);
4951: }
4952:
4953: /** Creates a ConcreteOpenDefDoc for an existing file f.
4954: * @return OpenDefinitionsDocument object for f
4955: * @throws FileNotFoundException if file f does not exist
4956: */
4957: protected ConcreteOpenDefDoc _createOpenDefinitionsDocument(File f)
4958: throws IOException {
4959: if (!f.exists())
4960: throw new FileNotFoundException("file " + f
4961: + " cannot be found");
4962: return new ConcreteOpenDefDoc(f);
4963: }
4964:
4965: /** Returns the OpenDefinitionsDocument corresponding to the given File, or null if that file is not open.
4966: * @param file File object to search for
4967: * @return Corresponding OpenDefinitionsDocument, or null
4968: */
4969: protected OpenDefinitionsDocument _getOpenDocument(File file) {
4970: synchronized (_documentsRepos) {
4971: return _documentsRepos.get(file);
4972: }
4973: }
4974:
4975: /** Returns the OpenDefinitionsDocuments that are NOT identified as project source files. */
4976: public List<OpenDefinitionsDocument> getNonProjectDocuments() {
4977: List<OpenDefinitionsDocument> allDocs = getOpenDefinitionsDocuments();
4978: List<OpenDefinitionsDocument> selectedDocs = new LinkedList<OpenDefinitionsDocument>();
4979: for (OpenDefinitionsDocument d : allDocs) {
4980: if (!d.inProjectPath() && !d.isAuxiliaryFile())
4981: selectedDocs.add(d);
4982: }
4983: return selectedDocs;
4984: }
4985:
4986: /** Returns the OpenDefinitionsDocuments that are identified as auxiliary project source files. */
4987: public List<OpenDefinitionsDocument> getAuxiliaryDocuments() {
4988: List<OpenDefinitionsDocument> allDocs = getOpenDefinitionsDocuments();
4989: List<OpenDefinitionsDocument> selectedDocs = new LinkedList<OpenDefinitionsDocument>();
4990: for (OpenDefinitionsDocument d : allDocs)
4991: if (d.isAuxiliaryFile())
4992: selectedDocs.add(d);
4993: return selectedDocs;
4994: }
4995:
4996: /** Returns the OpenDefinitionsDocuments that are identified as project source files. */
4997: public List<OpenDefinitionsDocument> getProjectDocuments() {
4998: List<OpenDefinitionsDocument> allDocs = getOpenDefinitionsDocuments();
4999: List<OpenDefinitionsDocument> projectDocs = new LinkedList<OpenDefinitionsDocument>();
5000: for (OpenDefinitionsDocument d : allDocs)
5001: if (d.inProjectPath() || d.isAuxiliaryFile())
5002: projectDocs.add(d);
5003: return projectDocs;
5004: }
5005:
5006: /* Extracts relative path (from project origin) to parent of file identified by path. Assumes path does not end in
5007: * File.separator. TODO: convert this method to take a File argument. */
5008: public String fixPathForNavigator(String path) throws IOException {
5009: String parent = path.substring(0, path
5010: .lastIndexOf(File.separator));
5011: String topLevelPath;
5012: String rootPath = getProjectRoot().getCanonicalPath();
5013:
5014: if (!parent.equals(rootPath)
5015: && !parent.startsWith(rootPath + File.separator))
5016: /** it's an external file, so don't give it a path */
5017: return "";
5018: else
5019: return parent.substring(rootPath.length());
5020: }
5021:
5022: /** Creates an OpenDefinitionsDocument for a file. Does not add to the navigator or notify that the file's open.
5023: * This method should be called only from within another open method that will do all of this clean up.
5024: * @param file the file to open
5025: */
5026: private OpenDefinitionsDocument _rawOpenFile(File file)
5027: throws IOException, AlreadyOpenException {
5028: OpenDefinitionsDocument openDoc = _getOpenDocument(file);
5029: if (openDoc != null)
5030: throw new AlreadyOpenException(openDoc); // handled in MainFrame.openFile(...)
5031: final ConcreteOpenDefDoc doc = _createOpenDefinitionsDocument(file);
5032: if (file instanceof DocFile) {
5033: DocFile df = (DocFile) file;
5034: Pair<Integer, Integer> scroll = df.getScroll();
5035: Pair<Integer, Integer> sel = df.getSelection();
5036: String pkg = df.getPackage();
5037: doc.setPackage(pkg); // Trust information in the project file; if it is wrong, _packageName invariant is broken
5038: doc.setInitialVScroll(scroll.first());
5039: doc.setInitialHScroll(scroll.second());
5040: doc.setInitialSelStart(sel.first());
5041: doc.setInitialSelEnd(sel.second());
5042: } else {
5043: // Utilities.show("Opened a file " + file.getName() + " that is not a DocFile");
5044: doc.setPackage(doc.getPackageNameFromDocument()); // get the package name from the file; forces file to be read
5045: }
5046: return doc;
5047: }
5048:
5049: /** This pop method enables an ArrayList to serve as stack. */
5050: protected static <T> T pop(ArrayList<T> stack) {
5051: return stack.remove(stack.size() - 1);
5052: }
5053:
5054: /** Creates an iNavigatorItem for a document, and adds it to the navigator. A helper for opening a file or creating
5055: * a new file.
5056: * @param doc the document to add to the navigator
5057: */
5058: protected void addDocToNavigator(final OpenDefinitionsDocument doc) {
5059: Utilities.invokeLater(new SRunnable() {
5060: public void run() {
5061: try {
5062: if (doc.isUntitled())
5063: _documentNavigator.addDocument(doc);
5064: else {
5065: String path = doc.getFile().getCanonicalPath();
5066: _documentNavigator.addDocument(doc,
5067: fixPathForNavigator(path));
5068: }
5069: } catch (IOException e) {
5070: _documentNavigator.addDocument(doc);
5071: }
5072: }
5073: });
5074: synchronized (_documentsRepos) {
5075: _documentsRepos.put(doc.getRawFile(), doc);
5076: }
5077: }
5078:
5079: /** Add a document to the classpath for the slave JVM. Does nothing here because there is no slave JVM. Overridden
5080: * in DefaultGlobalModel. */
5081: protected void addDocToClassPath(OpenDefinitionsDocument doc) {
5082: }
5083:
5084: /** Creates a document from a file.
5085: * @param file File to read document from
5086: * @return openened document
5087: */
5088: public OpenDefinitionsDocument _openFile(File file)
5089: throws IOException, AlreadyOpenException {
5090:
5091: OpenDefinitionsDocument doc = _rawOpenFile(file);
5092: _completeOpenFile(doc);
5093: return doc;
5094: }
5095:
5096: private void _completeOpenFile(OpenDefinitionsDocument d) {
5097: addDocToNavigator(d);
5098: addDocToClassPath(d);
5099:
5100: try {
5101: File f = d.getFile();
5102: if (!inProject(f) && inProjectPath(d)) {
5103: setProjectChanged(true);
5104: }
5105: } catch (FileMovedException fme) {
5106: /** project is not modified in this case */
5107: }
5108:
5109: _notifier.fileOpened(d);
5110: }
5111:
5112: private static class BackUpFileOptionListener implements
5113: OptionListener<Boolean> {
5114: public void optionChanged(OptionEvent<Boolean> oe) {
5115: Boolean value = oe.value;
5116: FileOps.DefaultFileSaver.setBackupsEnabled(value
5117: .booleanValue());
5118: }
5119: }
5120:
5121: //----------------------- SingleDisplay Methods -----------------------//
5122:
5123: /** Returns the currently active document. */
5124: public OpenDefinitionsDocument getActiveDocument() {
5125: return _activeDocument;
5126: }
5127:
5128: /** Sets the currently active document by updating the selection model.
5129: * @param doc Document to set as active
5130: */
5131: public void setActiveDocument(final OpenDefinitionsDocument doc) {
5132: /* The following code fixes a potential race because this method modifies the documentNavigator which is a swing
5133: * component. Hence it must run in the event thread. Note that setting the active document triggers the execution
5134: * of listeners some of which also need to run in the event thread.
5135: *
5136: * The _activeDoc field is set by _gainVisitor when the DocumentNavigator changes the active document.
5137: */
5138:
5139: // if (_activeDocument == doc) return; // this optimization appears to cause some subtle bugs
5140: // Utilities.showDebug("DEBUG: Called setActiveDocument()");
5141: try {
5142: Utilities.invokeAndWait(new SRunnable() {
5143: public void run() {
5144: // doc.makePositions(); // reconstruct the embedded postions in this document (reconstructs document if necesarry)
5145: _documentNavigator
5146: .setNextChangeModelInitiated(true);
5147: addToBrowserHistory();
5148: _documentNavigator.setActiveDoc(doc);
5149: }
5150: });
5151: } catch (Exception e) {
5152: throw new UnexpectedException(e);
5153: }
5154: }
5155:
5156: public Container getDocCollectionWidget() {
5157: return _documentNavigator.asContainer();
5158: }
5159:
5160: /** Sets the active document to be the next one in the collection. */
5161: public void setActiveNextDocument() {
5162: OpenDefinitionsDocument key = _activeDocument;
5163: OpenDefinitionsDocument nextKey = _documentNavigator
5164: .getNext(key);
5165: if (key != nextKey)
5166: setActiveDocument(nextKey);
5167: else
5168: setActiveDocument(_documentNavigator.getFirst());
5169: /* selects the active document in the navigator, which signals a listener to call _setActiveDoc(...) */
5170: }
5171:
5172: /** Sets the active document to be the previous one in the collection. */
5173: public void setActivePreviousDocument() {
5174: OpenDefinitionsDocument key = _activeDocument;
5175: OpenDefinitionsDocument prevKey = _documentNavigator
5176: .getPrevious(key);
5177: if (key != prevKey)
5178: setActiveDocument(prevKey);
5179: else
5180: setActiveDocument(_documentNavigator.getLast());
5181: /* selects the active document in the navigator, which signals a listener to call _setActiveDoc(...) */
5182: }
5183:
5184: //----------------------- End SingleDisplay Methods -----------------------//
5185:
5186: /** Returns whether there is currently only one open document which is untitled and unchanged. */
5187: private boolean _hasOneEmptyDocument() {
5188: return getOpenDefinitionsDocumentsSize() == 1
5189: && _activeDocument.isUntitled()
5190: && !_activeDocument.isModifiedSinceSave();
5191: }
5192:
5193: /** Creates a new document if there are currently no documents open. */
5194: private void _ensureNotEmpty() {
5195: if (getOpenDefinitionsDocumentsSize() == 0)
5196: newFile(getMasterWorkingDirectory());
5197: }
5198:
5199: /** Makes sure that none of the documents in the list are active.
5200: * Should only be executed in event thread.
5201: */
5202: private void _ensureNotActive(List<OpenDefinitionsDocument> docs) {
5203: if (docs.contains(getActiveDocument())) {
5204: // Find the one that should be the new active document
5205: IDocumentNavigator<OpenDefinitionsDocument> nav = getDocumentNavigator();
5206:
5207: OpenDefinitionsDocument item = docs.get(docs.size() - 1);
5208: OpenDefinitionsDocument nextActive = nav.getNext(item);
5209: if (!nextActive.equals(item)) {
5210: setActiveDocument(nextActive);
5211: return;
5212: }
5213:
5214: item = docs.get(0);
5215: nextActive = nav.getPrevious(item);
5216: if (!nextActive.equals(item)) {
5217: setActiveDocument(nextActive);
5218: return;
5219: }
5220:
5221: throw new RuntimeException(
5222: "No document to set active before closing");
5223: }
5224: }
5225:
5226: /** Sets the first document in the navigator as active. */
5227: public void setActiveFirstDocument() {
5228:
5229: /* Selects the active document in the navigator, which signals a listener to call _setActiveDoc(...). */
5230: setActiveDocument(getOpenDefinitionsDocuments().get(0));
5231: }
5232:
5233: private void _setActiveDoc(INavigatorItem idoc) {
5234: try {
5235: idoc.checkIfClassFileInSync();
5236: } catch (DocumentClosedException dce) { /* do nothing */
5237: }
5238: _activeDocument = (OpenDefinitionsDocument) idoc;
5239: // notify single display model listeners
5240: installActiveDocument();
5241: }
5242:
5243: /** Invokes the activeDocumentChanged method in the global listener on the argument _activeDocument. This process
5244: * sets up _activeDocument as the document in the definitions pane. */
5245: public void installActiveDocument() {
5246: _notifier.activeDocumentChanged(_activeDocument);
5247: }
5248:
5249: /** Invokes the activedocumentRefreshed method in the global listener on the argument _activeDocument. This process
5250: * refreshes the state of the _activeDocument as the document in the definitions pane. */
5251: public void refreshActiveDocument() {
5252: _notifier.activeDocumentRefreshed(_activeDocument);
5253: }
5254: }
|