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.util.text;
038:
039: import java.awt.print.*;
040:
041: import edu.rice.cs.drjava.model.print.DrJavaBook;
042:
043: import edu.rice.cs.util.StringOps;
044: import edu.rice.cs.util.UnexpectedException;
045: import edu.rice.cs.util.text.ConsoleDocumentInterface;
046: import edu.rice.cs.util.text.DocumentEditCondition;
047: import edu.rice.cs.util.text.EditDocumentException;
048:
049: /** A GUI-toolkit agnostic interface to a console document. This class assumes that the embedded document supports
050: * readers/writers locking and uses that locking protocol to ensure the integrity of the data added in this class
051: * @version $Id: ConsoleDocument.java 4255 2007-08-28 19:17:37Z mgricken $ */
052: public class ConsoleDocument implements ConsoleDocumentInterface {
053:
054: /** The default prompt to use in the console. */
055: public static final String DEFAULT_CONSOLE_PROMPT = "";
056:
057: /** Default text style. */
058: public static final String DEFAULT_STYLE = "default";
059:
060: /** Style for System.out */
061: public static final String SYSTEM_OUT_STYLE = "System.out";
062:
063: /** Style for System.err */
064: public static final String SYSTEM_ERR_STYLE = "System.err";
065:
066: /** Style for System.in */
067: public static final String SYSTEM_IN_STYLE = "System.in";
068:
069: /** The embedded document storing the text and _hasPrompt property for this console model. */
070: protected final ConsoleDocumentInterface _document;
071:
072: /** A runnable command to use for a notification beep. */
073: protected volatile Runnable _beep;
074:
075: /** Index in the document of the first place that is editable. */
076: private volatile int _promptPos;
077:
078: /** String to use for the prompt. */
079: protected volatile String _prompt;
080:
081: /** The book object used for printing that represents several pages */
082: protected volatile DrJavaBook _book;
083:
084: /** Creates a new ConsoleDocument with the given embedded ConsoleDocumentInterface (a SwingDocument in native DrJava).
085: * @param doc the embedded ConsoleDocumentInterface object
086: */
087: public ConsoleDocument(ConsoleDocumentInterface doc) {
088: _document = doc;
089:
090: _beep = new Runnable() {
091: public void run() {
092: }
093: };
094: _prompt = DEFAULT_CONSOLE_PROMPT;
095: _promptPos = DEFAULT_CONSOLE_PROMPT.length();
096: _document.setHasPrompt(false);
097:
098: // Prevent any edits before the prompt!
099: _document.setEditCondition(new ConsoleEditCondition());
100: }
101:
102: /** @return true iff this document has a prompt and is ready to accept input. */
103: public boolean hasPrompt() {
104: return _document.hasPrompt();
105: }
106:
107: public void setHasPrompt(boolean val) {
108: acquireWriteLock(); // Is this overkill?
109: try {
110: _document.setHasPrompt(val);
111: } finally {
112: releaseWriteLock();
113: }
114: }
115:
116: /** Accessor for the string used for the prompt. */
117: public String getPrompt() {
118: return _prompt;
119: }
120:
121: /** Sets the string to use for the prompt.
122: * @param prompt String to use for the prompt.
123: */
124: public void setPrompt(String prompt) {
125: acquireWriteLock();
126: _prompt = prompt;
127: releaseWriteLock();
128: }
129:
130: /** Gets the object which determines whether an insert/remove edit should be applied based on the inputs.
131: * @return the DocumentEditCondition to determine legality of inputs
132: */
133: public DocumentEditCondition getEditCondition() {
134: return _document.getEditCondition();
135: }
136:
137: /** Provides an object which can determine whether an insert or remove edit should be applied, based on
138: * the inputs.
139: * @param condition Object to determine legality of inputs
140: */
141: public void setEditCondition(DocumentEditCondition condition) {
142: _document.setEditCondition(condition);
143: }
144:
145: /** Returns the first location in the document where editing is allowed. */
146: public int getPromptPos() {
147: return _promptPos;
148: }
149:
150: /** Sets the prompt position. Only used in tests.
151: * @param newPos the new position.
152: */
153: public void setPromptPos(int newPos) {
154: acquireWriteLock();
155: _promptPos = newPos;
156: releaseWriteLock();
157: }
158:
159: /** Sets a runnable action to use as a beep.
160: * @param beep Runnable beep command
161: */
162: public void setBeep(Runnable beep) {
163: acquireWriteLock();
164: _beep = beep;
165: releaseWriteLock();
166: }
167:
168: /** Resets the document to a clean state. */
169: public void reset(String banner) {
170: acquireWriteLock();
171: try {
172: forceRemoveText(0, _document.getLength());
173: _forceInsertText(0, banner, DEFAULT_STYLE);
174: _promptPos = banner.length();
175: } catch (EditDocumentException e) {
176: throw new UnexpectedException(e);
177: } finally {
178: releaseWriteLock();
179: }
180: }
181:
182: /** Prints a prompt for a new input. */
183: public void insertPrompt() {
184: acquireWriteLock();
185: try {
186: int len = _document.getLength();
187: // Update _promptPos before updating _document because insertText runs insertUpdate to adjust caret
188: _promptPos = len + _prompt.length();
189: _forceInsertText(len, _prompt, DEFAULT_STYLE); // need forceAppend!
190: _document.setHasPrompt(true);
191: } catch (EditDocumentException e) {
192: throw new UnexpectedException(e);
193: } finally {
194: releaseWriteLock();
195: }
196: }
197:
198: /** Disables the prompt in this document. */
199: public void disablePrompt() {
200: acquireWriteLock();
201: try {
202: _document.setHasPrompt(false);
203: _promptPos = _document.getLength();
204: } finally {
205: releaseWriteLock();
206: }
207: }
208:
209: /** Inserts a new line at the given position.
210: * @param pos Position to insert the new line
211: */
212: public void insertNewLine(int pos) {
213: // Correct the position if necessary
214: acquireWriteLock();
215: try {
216: int len = _document.getLength();
217: if (pos > len)
218: pos = len;
219: else if (pos < 0)
220: pos = 0;
221:
222: String newLine = "\n"; // Was StringOps.EOL; but Swing uses '\n' for newLine
223: insertText(pos, newLine, DEFAULT_STYLE);
224: } catch (EditDocumentException e) {
225: throw new UnexpectedException(e);
226: } finally {
227: releaseWriteLock();
228: }
229: }
230:
231: /** Gets the position immediately before the prompt, or the doc length if there is no prompt. Assumes that ReadLock or
232: * WriteLock is already held.*/
233: private int _getPositionBeforePrompt() {
234: int len = _document.getLength();
235: if (_document.hasPrompt()) {
236: int promptStart = _promptPos - _prompt.length();
237: return (promptStart < len && promptStart >= 0) ? promptStart
238: : len; // ensure position is within document
239: }
240: return len;
241: }
242:
243: /** Inserts the given string with the given attributes just before the most recent prompt.
244: * @param text String to insert
245: * @param style name of style to format the string
246: */
247: public void insertBeforeLastPrompt(String text, String style) {
248: acquireWriteLock();
249: try {
250: int pos = _getPositionBeforePrompt();
251: _promptPos = _promptPos + text.length();
252: _forceInsertText(pos, text, style);
253: } catch (EditDocumentException ble) {
254: throw new UnexpectedException(ble);
255: } finally {
256: releaseWriteLock();
257: }
258: }
259:
260: /** Inserts a string into the document at the given offset and named style, if the edit condition allows it.
261: * @param offs Offset into the document
262: * @param str String to be inserted
263: * @param style Name of the style to use. Must have been added using addStyle.
264: * @throws EditDocumentException if the offset is illegal
265: */
266: public void insertText(int offs, String str, String style)
267: throws EditDocumentException {
268: acquireWriteLock();
269: try {
270: _insertText(offs, str, style);
271: } finally {
272: releaseWriteLock();
273: }
274: }
275:
276: /** Inserts a string into the document at the given offset and named style, if the edit condition allows it., as
277: * above. Assumes WriteLock is held. */
278: public void _insertText(int offs, String str, String style)
279: throws EditDocumentException {
280: if (offs < _promptPos)
281: _beep.run();
282: else {
283: _addToStyleLists(offs, str, style);
284: _document.insertText(offs, str, style);
285: }
286: }
287:
288: /** Appends a string to this in the given named style, if the edit condition allows it.
289: * @param str String to be inserted
290: * @param style Name of the style to use. Must have been added using addStyle.
291: * @throws EditDocumentException if the offset is illegal
292: */
293: public void append(String str, String style)
294: throws EditDocumentException {
295: acquireWriteLock();
296: try {
297: int offs = _document.getLength();
298: _addToStyleLists(offs, str, style);
299: _document._insertText(offs, str, style);
300: } finally {
301: releaseWriteLock();
302: }
303: }
304:
305: /** Inserts a string into the document at the given offset and style, regardless of the edit condition.
306: * @param offs Offset into the document
307: * @param str String to be inserted
308: * @param style Name of the style to use. Must have been added using addStyle.
309: * @throws EditDocumentException if the offset is illegal
310: */
311: public void forceInsertText(int offs, String str, String style)
312: throws EditDocumentException {
313: acquireWriteLock();
314: try {
315: _forceInsertText(offs, str, style);
316: } finally {
317: releaseWriteLock();
318: }
319: }
320:
321: /** Inserts a string into the document exactly like forceInsertText above except that it assumes the WriteLock
322: * is already held.
323: */
324: public void _forceInsertText(int offs, String str, String style)
325: throws EditDocumentException {
326: _addToStyleLists(offs, str, style);
327: _document._forceInsertText(offs, str, style);
328: }
329:
330: private void _addToStyleLists(int offs, String str, String style) {
331: if (_document instanceof SwingDocument)
332: ((SwingDocument) _document).addColoring(offs, offs
333: + str.length(), style);
334: }
335:
336: /** Removes a portion of the document, if the edit condition (including promptPos) allows it.
337: * @param offs Offset to start deleting from
338: * @param len Number of characters to remove
339: * @throws EditDocumentException if the offset or length are illegal
340: */
341: public void removeText(int offs, int len)
342: throws EditDocumentException {
343: acquireWriteLock();
344: try {
345: _removeText(offs, len);
346: } finally {
347: releaseWriteLock();
348: }
349: }
350:
351: /** Removes a portion of the document, if the edit condition allows it, as above. Assumes that WriteLock is held. */
352: public void _removeText(int offs, int len)
353: throws EditDocumentException {
354: if (offs < _promptPos)
355: _beep.run();
356: else
357: _document._removeText(offs, len);
358: }
359:
360: /** Removes a portion of the document, regardless of the edit condition.
361: * @param offs Offset to start deleting from
362: * @param len Number of characters to remove
363: * @throws EditDocumentException if the offset or length are illegal
364: */
365: public void forceRemoveText(int offs, int len)
366: throws EditDocumentException {
367: _document.forceRemoveText(offs, len);
368: }
369:
370: /** Returns the length of the document. */
371: public int getLength() {
372: return _document.getLength();
373: }
374:
375: /** Returns a portion of the document.
376: * @param offs First offset of the desired text
377: * @param len Number of characters to return
378: * @throws EditDocumentException if the offset or length are illegal
379: */
380: public String getDocText(int offs, int len)
381: throws EditDocumentException {
382: return _document.getDocText(offs, len);
383: }
384:
385: /** Returns the entire text of the document. Identical to getText() in AbstractDocumentInterface.
386: * @throws EditDocumentException if the offset or length are illegal
387: */
388: public String getText() {
389: acquireWriteLock();
390: try {
391: return _document.getDocText(0, getLength());
392: } finally {
393: releaseWriteLock();
394: }
395: }
396:
397: /** Returns the string that the user has entered at the current prompt.
398: * May contain newline characters.
399: */
400: public String getCurrentInput() {
401: acquireReadLock();
402: try {
403: try {
404: return getDocText(_promptPos, _document.getLength()
405: - _promptPos);
406: } catch (EditDocumentException e) {
407: throw new UnexpectedException(e);
408: }
409: } finally {
410: releaseReadLock();
411: }
412: }
413:
414: /** Clears the current input text. */
415: public void clearCurrentInput() {
416: _clearCurrentInputText();
417: }
418:
419: /** Removes the text from the current prompt to the end of the document. */
420: protected void _clearCurrentInputText() {
421: acquireWriteLock();
422: try {
423: // Delete old value of current line
424: removeText(_promptPos, _document.getLength() - _promptPos);
425: } catch (EditDocumentException ble) {
426: throw new UnexpectedException(ble);
427: } finally {
428: releaseWriteLock();
429: }
430: }
431:
432: /* Returns the default style for a "console" document. */
433: public String getDefaultStyle() {
434: return ConsoleDocument.DEFAULT_STYLE;
435: }
436:
437: /** Returns the Pageable object for printing.
438: * @return A Pageable representing this document.
439: */
440: public Pageable getPageable() throws IllegalStateException {
441: return _book;
442: }
443:
444: /** This method tells the document to prepare all the DrJavaBook and PagePrinter objects. */
445: public void preparePrintJob() {
446: _book = new DrJavaBook(getDocText(0, getLength()), "Console",
447: new PageFormat());
448: }
449:
450: /** Prints the given document by bringing up a "Print" window. */
451: public void print() throws PrinterException {
452: preparePrintJob();
453: PrinterJob printJob = PrinterJob.getPrinterJob();
454: printJob.setPageable(_book);
455: if (printJob.printDialog())
456: printJob.print();
457: cleanUpPrintJob();
458: }
459:
460: /** Clears the pageable object used to hold the print job. */
461: public void cleanUpPrintJob() {
462: _book = null;
463: }
464:
465: /** Class ensuring that attempts to edit document lines above the prompt are rejected. */
466: class ConsoleEditCondition extends DocumentEditCondition {
467: public boolean canInsertText(int offs) {
468: return canRemoveText(offs);
469: }
470:
471: public boolean canRemoveText(int offs) {
472: if (offs < _promptPos) {
473: _beep.run();
474: return false;
475: }
476: return true;
477: }
478: }
479:
480: /* Locking operations */
481:
482: /** Swing-style acquireReadLock(). */
483: public void acquireReadLock() {
484: _document.acquireReadLock();
485: }
486:
487: /** Swing-style releaseReadLock(). */
488: public void releaseReadLock() {
489: _document.releaseReadLock();
490: }
491:
492: /** Swing-style writeLock(). */
493: public void acquireWriteLock() {
494: _document.acquireWriteLock();
495: }
496:
497: /** Swing-style writeUnlock(). */
498: public void releaseWriteLock() {
499: _document.releaseWriteLock();
500: }
501:
502: // public int getLockState() { return _document.getLockState(); }
503: }
|