Source Code Cross Referenced for AbstractGlobalModel.java in  » IDE » DrJava » edu » rice » cs » drjava » model » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE » DrJava » edu.rice.cs.drjava.model 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.