001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.model;
038:
039: import java.awt.EventQueue;
040:
041: import java.io.File;
042: import java.io.FileFilter;
043: import java.io.IOException;
044: import java.io.OutputStream;
045:
046: import java.rmi.RemoteException;
047:
048: import java.util.ArrayList;
049: import java.util.LinkedList;
050: import java.util.List;
051: import java.util.Vector;
052: import java.util.StringTokenizer;
053: import java.util.Map;
054: import java.util.TreeMap;
055:
056: import javax.swing.text.BadLocationException;
057: import javax.swing.SwingUtilities;
058:
059: import edu.rice.cs.util.FileOpenSelector;
060: import edu.rice.cs.drjava.model.FileSaveSelector;
061: import edu.rice.cs.util.NullFile;
062: import edu.rice.cs.util.OperationCanceledException;
063: import edu.rice.cs.util.UnexpectedException;
064: import edu.rice.cs.util.newjvm.AbstractMasterJVM;
065: import edu.rice.cs.util.text.EditDocumentException;
066: import edu.rice.cs.util.swing.Utilities;
067: import edu.rice.cs.plt.reflect.JavaVersion;
068: import edu.rice.cs.plt.iter.IterUtil;
069: import edu.rice.cs.plt.io.IOUtil;
070:
071: import edu.rice.cs.drjava.DrJava;
072: import edu.rice.cs.drjava.config.OptionConstants;
073: import edu.rice.cs.drjava.config.OptionEvent;
074: import edu.rice.cs.drjava.config.OptionListener;
075: import edu.rice.cs.drjava.config.FileOption;
076:
077: import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException;
078: import edu.rice.cs.drjava.model.definitions.DefinitionsDocument;
079: import edu.rice.cs.drjava.model.definitions.InvalidPackageException;
080: import edu.rice.cs.drjava.model.debug.Breakpoint;
081: import edu.rice.cs.drjava.model.debug.Debugger;
082: import edu.rice.cs.drjava.model.debug.DebugException;
083: import edu.rice.cs.drjava.model.debug.NoDebuggerAvailable;
084: import edu.rice.cs.drjava.model.debug.DebugListener;
085: import edu.rice.cs.drjava.model.debug.DebugWatchData;
086: import edu.rice.cs.drjava.model.debug.DebugThreadData;
087: import edu.rice.cs.drjava.model.javadoc.JavadocModel;
088: import edu.rice.cs.drjava.model.javadoc.NoJavadocAvailable;
089: import edu.rice.cs.drjava.model.repl.DefaultInteractionsModel;
090: import edu.rice.cs.drjava.model.repl.DummyInteractionsListener;
091: import edu.rice.cs.drjava.model.repl.InteractionsDocument;
092: import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
093: import edu.rice.cs.drjava.model.repl.InteractionsListener;
094: import edu.rice.cs.drjava.model.repl.InteractionsScriptModel;
095: import edu.rice.cs.drjava.model.repl.newjvm.MainJVM;
096: import edu.rice.cs.drjava.model.compiler.CompilerListener;
097: import edu.rice.cs.drjava.model.compiler.CompilerModel;
098: import edu.rice.cs.drjava.model.compiler.DefaultCompilerModel;
099: import edu.rice.cs.drjava.model.compiler.CompilerInterface;
100: import edu.rice.cs.drjava.model.junit.DefaultJUnitModel;
101: import edu.rice.cs.drjava.model.junit.JUnitModel;
102: import edu.rice.cs.drjava.ui.MainFrame;
103:
104: import java.io.*;
105:
106: /** Handles the bulk of DrJava's program logic. The UI components interface with the GlobalModel through its public
107: * methods, and the GlobalModel responds via the GlobalModelListener interface. This removes the dependency on the
108: * UI for the logical flow of the program's features. With the current implementation, we can finally test the compile
109: * functionality of DrJava, along with many other things. <p>
110: * @version $Id: DefaultGlobalModel.java 4268 2007-11-29 00:52:17Z mgricken $
111: */
112: public class DefaultGlobalModel extends AbstractGlobalModel {
113:
114: /* FIELDS */
115:
116: /* static Log _log inherited from AbstractGlobalModel */
117:
118: /* Interpreter fields */
119:
120: /** The document used in the Interactions model. */
121: protected final InteractionsDJDocument _interactionsDocument;
122:
123: /** RMI interface to the Interactions JVM. */
124: final MainJVM _jvm;
125:
126: /** Interface between the InteractionsDocument and the JavaInterpreter, which runs in a separate JVM. */
127: protected final DefaultInteractionsModel _interactionsModel;
128:
129: /** Core listener attached to interactions model */
130: protected InteractionsListener _interactionsListener = new InteractionsListener() {
131: public void interactionStarted() {
132: }
133:
134: public void interactionEnded() {
135: }
136:
137: public void interactionErrorOccurred(int offset, int length) {
138: }
139:
140: public void interpreterResetting() {
141: }
142:
143: public void interpreterReady(File wd) {
144: File buildDir = _state.getBuildDirectory();
145: if (buildDir != null) {
146: // System.out.println("adding for reset: " + _state.getBuildDirectory().getAbsolutePath());
147: _jvm.addBuildDirectoryClassPath(IOUtil
148: .attemptAbsoluteFile(buildDir));
149: }
150: }
151:
152: public void interpreterResetFailed(Throwable t) {
153: }
154:
155: public void interpreterExited(int status) {
156: }
157:
158: public void interpreterChanged(boolean inProgress) {
159: }
160:
161: public void interactionIncomplete() {
162: }
163:
164: public void slaveJVMUsed() {
165: }
166: };
167:
168: private CompilerListener _clearInteractionsListener = new CompilerListener() {
169: public void compileStarted() {
170: }
171:
172: public void compileEnded(File workDir,
173: List<? extends File> excludedFiles) {
174: // Only clear interactions if there were no errors and unit testing is not in progress
175: if (((_compilerModel.getNumErrors() == 0) || (_compilerModel
176: .getCompilerErrorModel().hasOnlyWarnings()))
177: && !_junitModel.isTestInProgress()
178: && _resetAfterCompile) {
179: resetInteractions(workDir); // use same working directory as current interpreter
180: }
181: }
182:
183: public void saveBeforeCompile() {
184: }
185:
186: public void saveUntitled() {
187: }
188: };
189:
190: // ---- Compiler Fields ----
191:
192: /** CompilerModel manages all compiler functionality. */
193: private final CompilerModel _compilerModel;
194:
195: /** Whether or not to reset the interactions JVM after compiling. Should only be false in test cases. */
196: private volatile boolean _resetAfterCompile = true;
197:
198: /** Number of errors in last compilation. compilerModel._numErrors is trashed when the compile model is reset. */
199: private volatile int _numCompErrors = 0;
200:
201: /* JUnit Fields */
202:
203: /** JUnitModel manages all JUnit functionality. */
204: private final DefaultJUnitModel _junitModel;
205:
206: /* Javadoc Fields */
207:
208: /** Manages all Javadoc functionality. */
209: protected volatile JavadocModel _javadocModel;
210:
211: /* Debugger Fields */
212:
213: /** Interface to the integrated debugger. If unavailable, set NoDebuggerAvailable.ONLY. */
214: private volatile Debugger _debugger;
215:
216: /* CONSTRUCTORS */
217:
218: /** Constructs a new GlobalModel. Creates a new MainJVM and starts its Interpreter JVM. */
219: public DefaultGlobalModel() {
220: Iterable<? extends JDKToolsLibrary> tools = findLibraries();
221: List<CompilerInterface> compilers = new LinkedList<CompilerInterface>();
222: _debugger = null;
223: _javadocModel = null;
224: for (JDKToolsLibrary t : tools) {
225: if (t.compiler().isAvailable()) {
226: compilers.add(t.compiler());
227: }
228: if (_debugger == null && t.debugger().isAvailable()) {
229: _debugger = t.debugger();
230: }
231: if (_javadocModel == null && t.javadoc().isAvailable()) {
232: _javadocModel = t.javadoc();
233: }
234: }
235: if (_debugger == null) {
236: _debugger = NoDebuggerAvailable.ONLY;
237: }
238: if (_javadocModel == null) {
239: _javadocModel = new NoJavadocAvailable(this );
240: }
241:
242: File workDir = Utilities.TEST_MODE ? new File(System
243: .getProperty("user.home")) : getWorkingDirectory();
244: try {
245: _jvm = new MainJVM(workDir);
246: } catch (RemoteException e) {
247: throw new UnexpectedException(e);
248: }
249: // AbstractMasterJVM._log.log(this + " has created a new MainJVM");
250: _compilerModel = new DefaultCompilerModel(this , compilers);
251: _junitModel = new DefaultJUnitModel(_jvm, _compilerModel, this );
252: _interactionsDocument = new InteractionsDJDocument();
253:
254: _interactionsModel = new DefaultInteractionsModel(this , _jvm,
255: _interactionsDocument, workDir);
256: _interactionsModel.addListener(_interactionsListener);
257: _jvm.setInteractionsModel(_interactionsModel);
258: _jvm.setJUnitModel(_junitModel);
259:
260: _jvm.setOptionArgs(DrJava.getConfig()
261: .getSetting(SLAVE_JVM_ARGS));
262:
263: DrJava.getConfig().addOptionListener(SLAVE_JVM_ARGS,
264: new OptionListener<String>() {
265: public void optionChanged(OptionEvent<String> oe) {
266: _jvm.setOptionArgs(oe.value);
267: }
268: });
269:
270: _setupDebugger();
271:
272: // Chain notifiers so that all events also go to GlobalModelListeners.
273: _interactionsModel.addListener(_notifier);
274: _compilerModel.addListener(_notifier);
275: _junitModel.addListener(_notifier);
276: _javadocModel.addListener(_notifier);
277:
278: // Listen to compiler to clear interactions appropriately.
279: // XXX: The tests need this to be registered after _notifier, sadly.
280: // This is obnoxiously order-dependent, but it works for now.
281: _compilerModel.addListener(_clearInteractionsListener);
282:
283: // Note: starting the JVM in another thread does not appear to improve performance
284: // AbstractMasterJVM._log.log("Starting the interpreter in " + this);
285: _jvm.startInterpreterJVM();
286:
287: // Any lightweight parsing has been disabled until we have something that is beneficial and works better in the background.
288: // _parsingControl = new DefaultLightWeightParsingControl(this);
289: }
290:
291: private Iterable<JDKToolsLibrary> findLibraries() {
292: // Order to return: config setting, runtime (if different version), from search (if different versions)
293:
294: // We could give priority to libraries that have both available compilers and debuggers, but since this will
295: // almost always be true, it seems like more trouble than it is worth
296:
297: // map is sorted by version, lowest-to-highest
298: Map<JavaVersion, JDKToolsLibrary> results = new TreeMap<JavaVersion, JDKToolsLibrary>();
299:
300: File configTools = DrJava.getConfig()
301: .getSetting(JAVAC_LOCATION);
302: if (configTools != FileOption.NULL_FILE) {
303: JDKToolsLibrary fromConfig = JarJDKToolsLibrary
304: .makeFromFile(configTools, this );
305: if (fromConfig.isValid()) {
306: results.put(fromConfig.version().majorVersion(),
307: fromConfig);
308: }
309: }
310:
311: JDKToolsLibrary fromRuntime = JDKToolsLibrary
312: .makeFromRuntime(this );
313: JavaVersion runtimeVersion = fromRuntime.version()
314: .majorVersion();
315: if (fromRuntime.isValid()
316: && !results.containsKey(runtimeVersion)) {
317: results.put(runtimeVersion, fromRuntime);
318: }
319:
320: Iterable<JarJDKToolsLibrary> fromSearch = JarJDKToolsLibrary
321: .search(this );
322: for (JDKToolsLibrary t : fromSearch) {
323: JavaVersion tVersion = t.version().majorVersion();
324: // guaranteed to be valid
325: if (!results.containsKey(tVersion)) {
326: results.put(tVersion, t);
327: }
328: }
329:
330: return IterUtil.reverse(results.values());
331: }
332:
333: // public void compileAll() throws IOException{
334: //// ScrollableDialog sd = new ScrollableDialog(null, "DefaultGlobalModel.compileAll() called", "", "");
335: //// sd.show();
336: // _state.compileAll();
337: // }
338:
339: // public void junitAll() { _state.junitAll(); }
340:
341: /** Sets the build directory for a project. */
342: public void setBuildDirectory(File f) {
343: _state.setBuildDirectory(f);
344: if (f != null) {
345: // System.out.println("adding: " + f.getAbsolutePath());
346: _jvm.addBuildDirectoryClassPath(IOUtil
347: .attemptAbsoluteFile(f));
348: }
349:
350: _notifier.projectBuildDirChanged();
351: setProjectChanged(true);
352: setClassPathChanged(true);
353: }
354:
355: // ----- METHODS -----
356:
357: /** @return the interactions model. */
358: public DefaultInteractionsModel getInteractionsModel() {
359: return _interactionsModel;
360: }
361:
362: /** @return InteractionsDJDocument in use by the InteractionsDocument. */
363: public InteractionsDJDocument getSwingInteractionsDocument() {
364: return _interactionsDocument;
365: }
366:
367: public InteractionsDocument getInteractionsDocument() {
368: return _interactionsModel.getDocument();
369: }
370:
371: /** Gets the CompilerModel, which provides all methods relating to compilers. */
372: public CompilerModel getCompilerModel() {
373: return _compilerModel;
374: }
375:
376: /** Gets the JUnitModel, which provides all methods relating to JUnit testing. */
377: public JUnitModel getJUnitModel() {
378: return _junitModel;
379: }
380:
381: /** Gets the JavadocModel, which provides all methods relating to Javadoc. */
382: public JavadocModel getJavadocModel() {
383: return _javadocModel;
384: }
385:
386: public int getNumCompErrors() {
387: return _numCompErrors;
388: }
389:
390: public void setNumCompErrors(int num) {
391: _numCompErrors = num;
392: }
393:
394: /** Prepares this model to be thrown away. Never called in practice outside of quit(), except in tests. */
395: public void dispose() {
396: // Kill the interpreter
397: _jvm.killInterpreter(null);
398: // Commented out because it invokes UnicastRemoteObject.unexport
399: // try { _jvm.dispose(); }
400: // catch(RemoteException e) { /* ignore */ }
401: _notifier.removeAllListeners(); // removes the global model listeners!
402: }
403:
404: /** Disposes of external resources. Kills the slave JVM. */
405: public void disposeExternalResources() {
406: // Kill the interpreter
407: _jvm.killInterpreter(null);
408: }
409:
410: public void resetInteractions(File wd) {
411: resetInteractions(wd, false);
412: }
413:
414: /** Clears and resets the slave JVM with working directory wd. Also clears the console if the option is
415: * indicated (on by default). The reset operation is suppressed if the existing slave JVM has not been
416: * used, {@code wd} matches its working directory, and forceReset is false. {@code wd} may be {@code null}
417: * if a valid directory cannot be determined. In that case, the former working directory is used.
418: */
419: public void resetInteractions(File wd, boolean forceReset) {
420: File workDir = _interactionsModel.getWorkingDirectory();
421: if (wd == null) {
422: wd = workDir;
423: }
424:
425: if (!forceReset && !_jvm.slaveJVMUsed()
426: && !isClassPathChanged() && wd.equals(workDir)) {
427: // Eliminate resetting interpreter (slaveJVM) since it has already been reset appropriately.
428: _interactionsModel._notifyInterpreterReady(wd);
429: return;
430: }
431: // update the setting
432: DrJava.getConfig().setSetting(LAST_INTERACTIONS_DIRECTORY, wd);
433: _interactionsModel.resetInterpreter(wd);
434: }
435:
436: /** Interprets the current given text at the prompt in the interactions pane. */
437: public void interpretCurrentInteraction() {
438: _interactionsModel.interpretCurrentInteraction();
439: }
440:
441: /** Interprets file selected in the FileOpenSelector. Assumes strings have no trailing whitespace. Interpretation is
442: * aborted after the first error.
443: */
444: public void loadHistory(FileOpenSelector selector)
445: throws IOException {
446: _interactionsModel.loadHistory(selector);
447: }
448:
449: /** Loads the history/histories from the given selector. */
450: public InteractionsScriptModel loadHistoryAsScript(
451: FileOpenSelector selector) throws IOException,
452: OperationCanceledException {
453: return _interactionsModel.loadHistoryAsScript(selector);
454: }
455:
456: /** Clears the interactions history */
457: public void clearHistory() {
458: _interactionsModel.getDocument().clearHistory();
459: }
460:
461: /** Saves the unedited version of the current history to a file
462: * @param selector File to save to
463: */
464: public void saveHistory(FileSaveSelector selector)
465: throws IOException {
466: _interactionsModel.getDocument().saveHistory(selector);
467: }
468:
469: /** Saves the edited version of the current history to a file
470: * @param selector File to save to
471: * @param editedVersion Edited verison of the history which will be saved to file instead of the lines saved in
472: * the history. The saved file will still include any tags needed to recognize it as a history file.
473: */
474: public void saveHistory(FileSaveSelector selector,
475: String editedVersion) throws IOException {
476: _interactionsModel.getDocument().saveHistory(selector,
477: editedVersion);
478: }
479:
480: /** Returns the entire history as a String with semicolons as needed. */
481: public String getHistoryAsStringWithSemicolons() {
482: return _interactionsModel.getDocument()
483: .getHistoryAsStringWithSemicolons();
484: }
485:
486: /** Returns the entire history as a String. */
487: public String getHistoryAsString() {
488: return _interactionsModel.getDocument().getHistoryAsString();
489: }
490:
491: /** Called when the debugger wants to print a message. Inserts a newline. */
492: public void printDebugMessage(String s) {
493: _interactionsModel.getDocument().insertBeforeLastPrompt(
494: s + "\n", InteractionsDocument.DEBUGGER_STYLE);
495: }
496:
497: /** Blocks until the interpreter has registered. */
498: public void waitForInterpreter() {
499: _jvm.ensureInterpreterConnected();
500: }
501:
502: /** Returns the current classpath in use by the Interpreter JVM. */
503: public Iterable<File> getInteractionsClassPath() {
504: return _jvm.getClassPath();
505: }
506:
507: /** Sets whether or not the Interactions JVM will be reset after a compilation succeeds. This should ONLY be used
508: * in tests! This method is not supported by AbstractGlobalModel.
509: * @param shouldReset Whether to reset after compiling
510: */
511: void setResetAfterCompile(boolean shouldReset) {
512: _resetAfterCompile = shouldReset;
513: }
514:
515: /** Gets the Debugger used by DrJava. */
516: public Debugger getDebugger() {
517: return _debugger;
518: }
519:
520: /** Returns an available port number to use for debugging the interactions JVM.
521: * @throws IOException if unable to get a valid port number.
522: */
523: public int getDebugPort() throws IOException {
524: return _interactionsModel.getDebugPort();
525: }
526:
527: // ---------- ConcreteOpenDefDoc inner class ----------
528:
529: /** Inner class to handle operations on each of the open DefinitionsDocuments by the GlobalModel. <br><br>
530: * This was at one time called the <code>DefinitionsDocumentHandler</code>
531: * but was renamed (2004-Jun-8) to be more descriptive/intuitive.
532: */
533: class ConcreteOpenDefDoc extends
534: AbstractGlobalModel.ConcreteOpenDefDoc {
535: /** Standard constructor for a document read from a file. Initializes this ODD's DD.
536: * @param f file describing DefinitionsDocument to manage
537: */
538: ConcreteOpenDefDoc(File f) {
539: super (f);
540: }
541:
542: /* Standard constructor for a new document (no associated file) */
543: ConcreteOpenDefDoc(NullFile f) {
544: super (f);
545: }
546:
547: /** Starting compiling this document. Used only for unit testing */
548: public void startCompile() throws IOException {
549: _compilerModel.compile(ConcreteOpenDefDoc.this );
550: }
551:
552: private volatile InteractionsListener _runMain;
553:
554: /** Runs the main method in this document in the interactions pane after resetting interactions with the source
555: * root for this document as the working directory. Warns the use if the class files for the doucment are not
556: * up to date. Fires an event to signal when execution is about to begin.
557: * NOTE: this code normally runs in the event thread; it cannot block waiting for an event that is triggered by
558: * event thread execution!
559: * @exception ClassNameNotFoundException propagated from getFirstTopLevelClass()
560: * @exception IOException propagated from GlobalModel.compileAll()
561: */
562: public void runMain() throws ClassNameNotFoundException,
563: IOException {
564: assert EventQueue.isDispatchThread();
565:
566: // Get the class name for this document, the first top level class in the document.
567: final String className = getDocument()
568: .getQualifiedClassName();
569: final InteractionsDocument iDoc = _interactionsModel
570: .getDocument();
571: if (!checkIfClassFileInSync()) {
572: iDoc.insertBeforeLastPrompt(DOCUMENT_OUT_OF_SYNC_MSG,
573: InteractionsDocument.ERROR_STYLE);
574: return;
575: }
576:
577: final boolean wasDebuggerEnabled = getDebugger().isReady();
578:
579: _runMain = new DummyInteractionsListener() {
580: private boolean alreadyRun = false;
581:
582: public void interpreterReady(File wd) {
583: // prevent listener from running twice
584: if (alreadyRun)
585: return;
586: else
587: alreadyRun = true;
588: // Restart debugger if it was previously enabled and is now off
589: if (wasDebuggerEnabled
590: && (!getDebugger().isReady())) {
591: try {
592: getDebugger().startUp();
593: } catch (DebugException de) { /* ignore, continue without debugger */
594: }
595: }
596:
597: // Load the proper text into the interactions document
598: iDoc.clearCurrentInput();
599: iDoc.append("java " + className, null);
600:
601: // Finally, execute the new interaction and record that event
602: _interactionsModel.interpretCurrentInteraction();
603: _notifier.runStarted(ConcreteOpenDefDoc.this );
604:
605: // This used to be called using invokeLater, so that the listener would be removed
606: // after the read lock of the notifier had been released, but that was not always
607: // safe; the removal could still happen before the read lock was released
608: // Now removeListener has been rewritten and can be called even when the lock is
609: // held. In that case, the removal will be done as soon as possible.
610: _interactionsModel.removeListener(_runMain);
611: }
612: };
613:
614: _interactionsModel.addListener(_runMain);
615:
616: File workDir;
617: if (isProjectActive())
618: workDir = getWorkingDirectory(); // use working directory for project
619: else {
620: // use source root of current document
621: try {
622: workDir = getSourceRoot();
623: } catch (InvalidPackageException e) {
624: workDir = null;
625: }
626: }
627: // Reset interactions to the soure root for this document; class will be executed when new interpreter is ready
628: resetInteractions(workDir);
629: }
630:
631: /** Runs JUnit on the current document. Requires that all source documents are compiled before proceeding. */
632: public void startJUnit() throws ClassNotFoundException,
633: IOException {
634: _junitModel.junit(this );
635: }
636:
637: /** Generates Javadoc for this document, saving the output to a temporary directory. The location is provided to
638: * the javadocEnded event on the given listener.
639: * java@param saver FileSaveSelector for saving the file if it needs to be saved
640: */
641: public void generateJavadoc(FileSaveSelector saver)
642: throws IOException {
643: // Use the model's classpath, and use the EventNotifier as the listener
644: _javadocModel.javadocDocument(this , saver);
645: }
646:
647: /** Called to indicate the document is being closed, so to remove all related state from the debug manager. */
648: public void removeFromDebugger() {
649: while (getBreakpointManager().getRegions().size() > 0) {
650: Breakpoint bp = getBreakpointManager().getRegions()
651: .get(0);
652: getBreakpointManager().removeRegion(bp);
653: }
654: }
655: } /* End of ConcreteOpenDefDoc */
656:
657: /** Creates a ConcreteOpenDefDoc for a new DefinitionsDocument.
658: * @return OpenDefinitionsDocument object for a new document
659: */
660: protected ConcreteOpenDefDoc _createOpenDefinitionsDocument(
661: NullFile f) {
662: return new ConcreteOpenDefDoc(f);
663: }
664:
665: /** Creates a ConcreteOpenDefDoc for a given file f
666: * @return OpenDefinitionsDocument object for f
667: */
668: protected ConcreteOpenDefDoc _createOpenDefinitionsDocument(File f)
669: throws IOException {
670: if (!f.exists())
671: throw new FileNotFoundException("file " + f
672: + " cannot be found");
673: return new ConcreteOpenDefDoc(f);
674: }
675:
676: /** Adds the source root for doc to the interactions classpath; this function is a helper to _openFiles.
677: * @param doc the document to add to the classpath
678: */
679: protected void addDocToClassPath(OpenDefinitionsDocument doc) {
680: try {
681: File sourceRoot = doc.getSourceRoot();
682: if (doc.isAuxiliaryFile()) {
683: _interactionsModel.addProjectFilesClassPath(sourceRoot);
684: } else {
685: _interactionsModel
686: .addExternalFilesClassPath(sourceRoot);
687: }
688: setClassPathChanged(true);
689: } catch (InvalidPackageException e) {
690: // Invalid package-- don't add it to classpath
691: }
692: }
693:
694: private void _setupDebugger() {
695: _jvm.setDebugModel(_debugger.callback());
696:
697: // add listener to set the project file to "changed" when a breakpoint or watch is added, removed, or changed
698: getBreakpointManager().addListener(
699: new RegionManagerListener<Breakpoint>() {
700: public void regionAdded(final Breakpoint bp,
701: int index) {
702: setProjectChanged(true);
703: }
704:
705: public void regionChanged(final Breakpoint bp,
706: int index) {
707: setProjectChanged(true);
708: }
709:
710: public void regionRemoved(final Breakpoint bp) {
711: try {
712: getDebugger().removeBreakpoint(bp);
713: } catch (DebugException de) { /* just ignore it */
714: }
715: setProjectChanged(true);
716: }
717: });
718: getBookmarkManager().addListener(
719: new RegionManagerListener<DocumentRegion>() {
720: public void regionAdded(DocumentRegion r, int index) {
721: setProjectChanged(true);
722: }
723:
724: public void regionChanged(DocumentRegion r,
725: int index) {
726: setProjectChanged(true);
727: }
728:
729: public void regionRemoved(DocumentRegion r) {
730: setProjectChanged(true);
731: }
732: });
733:
734: _debugger.addListener(new DebugListener() {
735: public void watchSet(final DebugWatchData w) {
736: setProjectChanged(true);
737: }
738:
739: public void watchRemoved(final DebugWatchData w) {
740: setProjectChanged(true);
741: }
742:
743: public void regionAdded(final Breakpoint bp, int index) {
744: }
745:
746: public void regionChanged(final Breakpoint bp, int index) {
747: }
748:
749: public void regionRemoved(final Breakpoint bp) {
750: }
751:
752: public void debuggerStarted() {
753: }
754:
755: public void debuggerShutdown() {
756: }
757:
758: public void threadLocationUpdated(
759: OpenDefinitionsDocument doc, int lineNumber,
760: boolean shouldHighlight) {
761: }
762:
763: public void breakpointReached(final Breakpoint bp) {
764: }
765:
766: public void stepRequested() {
767: }
768:
769: public void currThreadSuspended() {
770: }
771:
772: public void currThreadResumed() {
773: }
774:
775: public void threadStarted() {
776: }
777:
778: public void currThreadDied() {
779: }
780:
781: public void nonCurrThreadDied() {
782: }
783:
784: public void currThreadSet(DebugThreadData thread) {
785: }
786: });
787: }
788:
789: /** Get the class path to be used in all class-related operations.
790: * TODO: Insure that this is used wherever appropriate.
791: */
792: public Iterable<File> getClassPath() {
793: Iterable<File> result = IterUtil.empty();
794:
795: if (isProjectActive()) {
796: File buildDir = getBuildDirectory();
797: if (buildDir != null) {
798: result = IterUtil.compose(result, buildDir);
799: }
800:
801: /* We prefer to assume the project root is the project's source root, rather than
802: * checking *every* file in the project for its source root. This is a bit problematic,
803: * because "Compile Project" won't care if the user has multiple source roots (or even just a
804: * single "src" subdirectory), and the user in this situation (assuming the build dir is
805: * null) wouldn't notice a problem until trying to access the compiled classes in the
806: * Interactions.
807: */
808: File projRoot = getProjectRoot();
809: if (projRoot != null) {
810: result = IterUtil.compose(result, projRoot);
811: }
812:
813: Iterable<File> projectExtras = getExtraClassPath();
814: if (projectExtras != null) {
815: result = IterUtil.compose(result, projectExtras);
816: }
817: } else {
818: result = IterUtil.compose(result, getSourceRootSet());
819: }
820:
821: Vector<File> globalExtras = DrJava.getConfig().getSetting(
822: EXTRA_CLASSPATH);
823: if (globalExtras != null) {
824: result = IterUtil.compose(result, globalExtras);
825: }
826:
827: /* We must add JUnit to the class path. We do so by including the current JVM's class path.
828: * This is not ideal, because all other classes on the current class path (including all of DrJava's
829: * internal classes) are also included. But we're probably stuck doing something like this if we
830: * want to continue bundling JUnit with DrJava.
831: */
832: result = IterUtil.compose(result, RUNTIME_CLASS_PATH);
833:
834: return result;
835: }
836:
837: /** Adds the project root (if a project is open), the source roots for other open documents, the paths in the
838: * "extra classpath" config option, as well as any project-specific classpaths to the interpreter's classpath.
839: * This method is called in DefaultInteractionsModel when the interpreter becomes ready.
840: */
841: public void resetInteractionsClassPath() {
842: Iterable<File> projectExtras = getExtraClassPath();
843: //System.out.println("Adding project classpath vector to interactions classpath: " + projectExtras);
844: if (projectExtras != null)
845: for (File cpE : projectExtras) {
846: _interactionsModel.addProjectClassPath(cpE);
847: }
848:
849: Vector<File> cp = DrJava.getConfig()
850: .getSetting(EXTRA_CLASSPATH);
851: if (cp != null) {
852: for (File f : cp) {
853: _interactionsModel.addExtraClassPath(f);
854: }
855: }
856:
857: for (OpenDefinitionsDocument odd : getAuxiliaryDocuments()) {
858: // this forwards directly to InterpreterJVM.addClassPath(String)
859: try {
860: _interactionsModel.addProjectFilesClassPath(odd
861: .getSourceRoot());
862: } catch (InvalidPackageException e) { /* ignore it */
863: }
864: }
865:
866: for (OpenDefinitionsDocument odd : getNonProjectDocuments()) {
867: // this forwards directly to InterpreterJVM.addClassPath(String)
868: try {
869: File sourceRoot = odd.getSourceRoot();
870: if (sourceRoot != null)
871: _interactionsModel
872: .addExternalFilesClassPath(sourceRoot);
873: } catch (InvalidPackageException e) { /* ignore it */
874: }
875: }
876:
877: // add project source root to projectFilesClassPath. All files in project tree have this root.
878:
879: _interactionsModel.addProjectFilesClassPath(getProjectRoot());
880: setClassPathChanged(false); // reset classPathChanged state
881: }
882:
883: // private class ExtraClasspathOptionListener implements OptionListener<Vector<File>> {
884: // public void optionChanged (OptionEvent<Vector<File>> oce) {
885: // Vector<File> cp = oce.value;
886: // if (cp != null) {
887: // for (File f: cp) {
888: // // this forwards directly to InterpreterJVM.addClassPath(String)
889: // try { _interactionsModel.addExtraClassPath(f.toURL()); }
890: // catch(MalformedURLException murle) {
891: // /* do nothing; findbugs signals a bug unless this catch clause spans more than two lines */
892: // }
893: // }
894: // }
895: // }
896: // }
897:
898: }
|