KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > debug > internal > ui > views > console > ConsoleDocumentPartitioner


1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.debug.internal.ui.views.console;
13
14
15 import java.io.IOException JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Collections JavaDoc;
18 import java.util.Comparator JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Vector JavaDoc;
22
23 import org.eclipse.debug.core.DebugEvent;
24 import org.eclipse.debug.core.DebugPlugin;
25 import org.eclipse.debug.core.IDebugEventSetListener;
26 import org.eclipse.debug.core.IStreamListener;
27 import org.eclipse.debug.core.model.IFlushableStreamMonitor;
28 import org.eclipse.debug.core.model.IProcess;
29 import org.eclipse.debug.core.model.IStreamMonitor;
30 import org.eclipse.debug.core.model.IStreamsProxy;
31 import org.eclipse.debug.internal.ui.DebugUIPlugin;
32 import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
33 import org.eclipse.debug.ui.DebugUITools;
34 import org.eclipse.debug.ui.IDebugUIConstants;
35 import org.eclipse.debug.ui.console.IConsole;
36 import org.eclipse.debug.ui.console.IConsoleColorProvider;
37 import org.eclipse.debug.ui.console.IConsoleHyperlink;
38 import org.eclipse.jface.preference.IPreferenceStore;
39 import org.eclipse.jface.text.BadLocationException;
40 import org.eclipse.jface.text.BadPositionCategoryException;
41 import org.eclipse.jface.text.DocumentEvent;
42 import org.eclipse.jface.text.IDocument;
43 import org.eclipse.jface.text.IDocumentPartitioner;
44 import org.eclipse.jface.text.IDocumentPartitionerExtension;
45 import org.eclipse.jface.text.IRegion;
46 import org.eclipse.jface.text.ITypedRegion;
47 import org.eclipse.jface.text.Position;
48 import org.eclipse.jface.text.Region;
49 import org.eclipse.jface.util.IPropertyChangeListener;
50 import org.eclipse.jface.util.PropertyChangeEvent;
51 import org.eclipse.swt.widgets.Display;
52 import org.eclipse.ui.console.ConsolePlugin;
53
54 /**
55  * Default console document partitioner. Partitions a document into
56  * color regions for standard in, out, err.
57  */

58 public class ConsoleDocumentPartitioner implements IDocumentPartitioner, IDocumentPartitionerExtension, IPropertyChangeListener, IConsole, IDebugEventSetListener {
59
60     protected IProcess fProcess;
61     protected IConsoleColorProvider fColorProvider;
62     private IStreamsProxy fProxy;
63     protected List JavaDoc fStreamListeners = new ArrayList JavaDoc(2);
64     
65     private String JavaDoc[] fSortedLineDelimiters;
66     
67     // high and low water marks for buffering output
68
private boolean fUpdatingBuffer = false;
69     private int fLowWaterMark;
70     private int fHighWaterMark;
71     
72     // max amount of output (characters) processed per poll
73
private int fMaxAppendSize;
74     
75     class StreamEntry {
76         /**
77          * Identifier of the stream written to.
78          */

79         private String JavaDoc fStreamIdentifier;
80         /**
81          * The text written
82          */

83         private String JavaDoc fText = null;
84         
85         StreamEntry(String JavaDoc text, String JavaDoc streamIdentifier) {
86             fText = text;
87             fStreamIdentifier = streamIdentifier;
88         }
89         
90         /**
91          * Returns the stream identifier
92          */

93         public String JavaDoc getStreamIdentifier() {
94             return fStreamIdentifier;
95         }
96         
97         /**
98          * Returns the text written
99          */

100         public String JavaDoc getText() {
101             return fText;
102         }
103         
104         public boolean isClosedEntry() {
105             return false;
106         }
107     }
108     
109     /**
110      * A stream entry representing stream closure
111      */

112     class StreamsClosedEntry extends StreamEntry {
113         StreamsClosedEntry() {
114             super("", ""); //$NON-NLS-1$ //$NON-NLS-2$
115
}
116         
117         public boolean isClosedEntry() {
118             return true;
119         }
120     }
121     
122     class StreamListener implements IStreamListener {
123         
124         private String JavaDoc fStreamIdentifier;
125         private IStreamMonitor fStreamMonitor;
126         private boolean fIsSystemOut = false;
127         private boolean fIsSystemErr = false;
128     
129         public StreamListener(String JavaDoc streamIdentifier, IStreamMonitor streamMonitor) {
130             fStreamIdentifier = streamIdentifier;
131             fStreamMonitor = streamMonitor;
132             fIsSystemOut = IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM.equals(streamIdentifier);
133             fIsSystemErr = IDebugUIConstants.ID_STANDARD_ERROR_STREAM.equals(streamIdentifier);
134         }
135         
136         public void streamAppended(String JavaDoc newText, IStreamMonitor monitor) {
137             if (fIsSystemOut) {
138                 DebugUIPlugin.getDefault().getConsoleDocumentManager().aboutToWriteSystemOut(getProcess());
139             } else if (fIsSystemErr) {
140                 DebugUIPlugin.getDefault().getConsoleDocumentManager().aboutToWriteSystemErr(getProcess());
141             }
142             ConsoleDocumentPartitioner.this.streamAppended(newText, fStreamIdentifier);
143         }
144         
145         public void streamClosed(IStreamMonitor monitor) {
146             //ConsoleDocumentPartitioner.this.streamClosed(fStreamIdentifier);
147
}
148         
149         public void connect() {
150             fStreamMonitor.addListener(this);
151             String JavaDoc contents= fStreamMonitor.getContents();
152             if (fStreamMonitor instanceof IFlushableStreamMonitor) {
153                 // flush the underlying buffer and do not duplicate storage
154
IFlushableStreamMonitor flushableStreamMonitor = (IFlushableStreamMonitor)fStreamMonitor;
155                 flushableStreamMonitor.flushContents();
156                 flushableStreamMonitor.setBuffered(false);
157             }
158             if (contents.length() > 0) {
159                 streamAppended(contents, fStreamMonitor);
160             }
161         }
162         
163         public void disconnect() {
164             fStreamMonitor.removeListener(this);
165         }
166     }
167
168     
169     /**
170      * A queue of stream entries written to standard out and standard err.
171      * Entries appended to the end of the queue and removed from the front.
172      * Intentionally a vector to obtain synchronization as entries are
173      * added and removed.
174      */

175     private Vector JavaDoc fQueue = new Vector JavaDoc(10);
176     
177     /**
178      * Thread that polls the queue for new output
179      */

180     private Thread JavaDoc fPollingThread = null;
181     
182     /**
183      * Whether an append is still in progress or to be run
184      */

185     private boolean fAppending = false;
186     
187     /**
188      * Whether the console has been killed/disconnected
189      */

190     private boolean fKilled = false;
191     
192     /**
193      * Whether to keep polling
194      */

195     private boolean fPoll = false;
196     
197     /**
198      * Whether the streams coonnected to the associated process are closed
199      */

200     private boolean fClosed= false;
201     
202     /**
203      * The associated document
204      */

205     private IDocument fDocument = null;
206     
207     /**
208      * The length of the current line
209      */

210     private int fLineLength = 0;
211     
212     /**
213      * Maximum line length before wrapping.
214      */

215     private int fMaxLineLength = 80;
216     
217     /**
218      * Whether using auto-wrap mode
219      */

220     private boolean fWrap = false;
221     
222     /**
223      * List of partitions
224      */

225     private List JavaDoc fPartitions = new ArrayList JavaDoc(5);
226     
227     /**
228      * The base number of milliseconds to pause
229      * between polls.
230      */

231     private static final long BASE_DELAY= 100L;
232     
233     /**
234      * The identifier of the stream that was last appended to
235      */

236     private String JavaDoc fLastStreamIdentifier= null;
237     
238     /**
239      * Keyboard input buffer
240      */

241     private StringBuffer JavaDoc fInputBuffer = new StringBuffer JavaDoc();
242     
243     /**
244      * Queue of hyperlinks to be added to the console
245      */

246     private Vector JavaDoc fPendingLinks = new Vector JavaDoc();
247     
248     /**
249      * The line notifier associated with this partitioner or <code>null</code> if none
250      */

251     private ConsoleLineNotifier fLineNotifier = null;
252
253     /**
254      * @see org.eclipse.jface.text.IDocumentPartitioner#connect(org.eclipse.jface.text.IDocument)
255      */

256     public void connect(IDocument document) {
257         fDocument = document;
258         fDocument.addPositionCategory(HyperlinkPosition.HYPER_LINK_CATEGORY);
259         document.setDocumentPartitioner(this);
260         IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
261         fWrap = store.getBoolean(IDebugPreferenceConstants.CONSOLE_WRAP);
262         fMaxLineLength = store.getInt(IDebugPreferenceConstants.CONSOLE_WIDTH);
263         store.addPropertyChangeListener(this);
264         fColorProvider.connect(fProcess, this);
265         DebugPlugin.getDefault().addDebugEventListener(this);
266         if (fProcess.isTerminated()) {
267             // it is possible the terminate event will have been fired before the
268
// document is connected - in this case, ensure we have closed the streams
269
// and notified the line tracker
270
streamsClosed();
271         }
272     }
273
274     /**
275      * @see org.eclipse.jface.text.IDocumentPartitioner#disconnect()
276      */

277     public void disconnect() {
278         kill();
279         if (fLineNotifier != null) {
280             fLineNotifier.disconnect();
281         }
282         fColorProvider.disconnect();
283         fDocument.setDocumentPartitioner(null);
284         DebugPlugin.getDefault().removeDebugEventListener(this);
285     }
286
287     /**
288      * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
289      */

290     public void documentAboutToBeChanged(DocumentEvent event) {
291     }
292
293     /**
294      * @see org.eclipse.jface.text.IDocumentPartitioner#documentChanged(org.eclipse.jface.text.DocumentEvent)
295      */

296     public boolean documentChanged(DocumentEvent event) {
297         return documentChanged2(event) != null;
298     }
299
300     /**
301      * @see org.eclipse.jface.text.IDocumentPartitioner#getLegalContentTypes()
302      */

303     public String JavaDoc[] getLegalContentTypes() {
304         return new String JavaDoc[] {InputPartition.INPUT_PARTITION_TYPE, OutputPartition.OUTPUT_PARTITION_TYPE, BreakPartition.BREAK_PARTITION_TYPE};
305     }
306
307     /**
308      * @see org.eclipse.jface.text.IDocumentPartitioner#getContentType(int)
309      */

310     public String JavaDoc getContentType(int offset) {
311         ITypedRegion partition = getPartition(offset);
312         if (partition != null) {
313             return partition.getType();
314         }
315         return null;
316     }
317
318     /**
319      * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
320      */

321     public ITypedRegion[] computePartitioning(int offset, int length) {
322         if (offset == 0 && length == fDocument.getLength()) {
323             return (ITypedRegion[])fPartitions.toArray(new ITypedRegion[fPartitions.size()]);
324         } else {
325             int end = offset + length;
326             List JavaDoc list = new ArrayList JavaDoc();
327             for (int i = 0; i < fPartitions.size(); i++) {
328                 ITypedRegion partition = (ITypedRegion)fPartitions.get(i);
329                 int partitionStart = partition.getOffset();
330                 int partitionEnd = partitionStart + partition.getLength();
331                 if ((offset >= partitionStart && offset <= partitionEnd) ||
332                     (offset < partitionStart && end >= partitionStart)) {
333                         list.add(partition);
334                 }
335             }
336             return (ITypedRegion[])list.toArray(new ITypedRegion[list.size()]);
337         }
338     }
339
340     /**
341      * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
342      */

343     public ITypedRegion getPartition(int offset) {
344         for (int i = 0; i < fPartitions.size(); i++) {
345             ITypedRegion partition = (ITypedRegion)fPartitions.get(i);
346             int start = partition.getOffset();
347             int end = start + partition.getLength();
348             if (offset >= start && offset < end) {
349                 return partition;
350             }
351         }
352         return null;
353     }
354
355     /**
356      * @see org.eclipse.jface.text.IDocumentPartitionerExtension#documentChanged2(org.eclipse.jface.text.DocumentEvent)
357      */

358     public IRegion documentChanged2(DocumentEvent event) {
359         if (fUpdatingBuffer) {
360             return new Region(0, fDocument.getLength());
361         }
362         addPendingLinks();
363         String JavaDoc text = event.getText();
364         if (isAppendInProgress()) {
365             // stream input
366
addPartition(new OutputPartition(fLastStreamIdentifier, event.getOffset(), text.length()));
367             if (fLineNotifier != null) {
368                 fLineNotifier.consoleChanged(event);
369             }
370         } else {
371             // console keyboard input
372
int amountDeleted = event.getLength() - text.length();
373             int docLength = fDocument.getLength();
374             int bufferStartOffset = docLength + amountDeleted - fInputBuffer.length();
375             int bufferModifyOffset = event.getOffset() - bufferStartOffset;
376             int bufferModifyOffsetEnd = bufferModifyOffset + event.getLength();
377             
378             if (docLength == 0) {
379                 // cleared
380
fQueue.clear();
381                 fInputBuffer.setLength(0);
382                 fPartitions.clear();
383                 // reset lines processed to 0
384
if (fLineNotifier != null) {
385                     fLineNotifier.setLinesProcessed(0);
386                 }
387                 // remove existing positions
388
try {
389                     Position[] positions = fDocument.getPositions(HyperlinkPosition.HYPER_LINK_CATEGORY);
390                     for (int i = 0; i < positions.length; i++) {
391                         Position position = positions[i];
392                         fDocument.removePosition(HyperlinkPosition.HYPER_LINK_CATEGORY, position);
393                     }
394                 } catch (BadPositionCategoryException e) {
395                 }
396                 return new Region(0,0);
397             }
398                         
399             if (amountDeleted > 0) {
400                 // deletion
401
fInputBuffer.replace(bufferModifyOffset, bufferModifyOffsetEnd, text);
402                 // replace the last partition
403
InputPartition partition = new InputPartition(IDebugUIConstants.ID_STANDARD_INPUT_STREAM, bufferStartOffset, fInputBuffer.length());
404                 fPartitions.set(fPartitions.size() - 1, partition);
405             } else {
406                 // insert/replace - must process entire buffer in case of
407
// line delimiter insertion in middle of buffer
408

409                 // parse for line delimiters (indicate chunks to write to standard in)
410
String JavaDoc[] lineDelimiters= getLegalLineDelimiters();
411                 StringBuffer JavaDoc temp =new StringBuffer JavaDoc(fInputBuffer.toString());
412                 temp.replace(bufferModifyOffset, bufferModifyOffsetEnd, text);
413                 String JavaDoc remaining = temp.toString();
414                 int partitionOffset = bufferStartOffset;
415                 fInputBuffer.setLength(0);
416                 boolean includesLF = false;
417                 // line delimiters are sorted by length (compare longest ones first)
418
for (int i= lineDelimiters.length - 1; i >= 0; i--) {
419                     int lf = remaining.indexOf(lineDelimiters[i]);
420                     while (lf >= 0) {
421                         includesLF = true;
422                         int split = lf + lineDelimiters[i].length();
423                         fInputBuffer.append(remaining.substring(0, split));
424                         remaining = remaining.substring(split);
425                         String JavaDoc buffer = fInputBuffer.toString();
426                         fInputBuffer.setLength(0);
427                         InputPartition written = (InputPartition)addPartition(new InputPartition(IDebugUIConstants.ID_STANDARD_INPUT_STREAM, partitionOffset, split));
428                         written.setReadOnly(true);
429                         partitionOffset += split;
430                         addPartition(new InputPartition(IDebugUIConstants.ID_STANDARD_INPUT_STREAM, partitionOffset, 0));
431                         if (fProxy != null) {
432                             try {
433                                 fProxy.write(buffer);
434                             } catch (IOException JavaDoc ioe) {
435                                 DebugUIPlugin.log(ioe);
436                             }
437                         }
438                         lf = remaining.indexOf(lineDelimiters[i]);
439                     }
440                     if (includesLF) {
441                         break;
442                     }
443                 }
444                 if (remaining.length() > 0) {
445                     fInputBuffer.append(remaining);
446                     addPartition(new InputPartition(IDebugUIConstants.ID_STANDARD_INPUT_STREAM, partitionOffset, remaining.length()));
447                 }
448             }
449         }
450         ITypedRegion[] affectedRegions = computePartitioning(event.getOffset(), text.length());
451         if (affectedRegions.length == 0) {
452             return null;
453         }
454         if (affectedRegions.length == 1) {
455             return affectedRegions[0];
456         }
457         int affectedLength = affectedRegions[0].getLength();
458         for (int i = 1; i < affectedRegions.length; i++) {
459             ITypedRegion region = affectedRegions[i];
460             affectedLength += region.getLength();
461         }
462         return new Region(affectedRegions[0].getOffset(), affectedLength);
463     }
464
465     /**
466      * Adds a new colored input partition, combining with the previous partition if
467      * possible.
468      */

469     protected StreamPartition addPartition(StreamPartition partition) {
470         if (fPartitions.isEmpty()) {
471             fPartitions.add(partition);
472         } else {
473             int index = fPartitions.size() - 1;
474             StreamPartition last = (StreamPartition)fPartitions.get(index);
475             if (last.canBeCombinedWith(partition)) {
476                 // replace with a single partition
477
partition = last.combineWith(partition);
478                 fPartitions.set(index, partition);
479             } else {
480                 // different kinds - add a new parition
481
fPartitions.add(partition);
482             }
483         }
484         return partition;
485     }
486     
487     /**
488      * Add any pending links to the document that are now within the document's
489      * bounds.
490      */

491     protected void addPendingLinks() {
492         synchronized (fPendingLinks) {
493             if (fPendingLinks.isEmpty()) {
494                 return;
495             }
496             Iterator JavaDoc links = fPendingLinks.iterator();
497             while (links.hasNext()) {
498                 HyperlinkPosition link = (HyperlinkPosition)links.next();
499                 if ((link.getOffset() + link.getLength()) <= fDocument.getLength()) {
500                     links.remove();
501                     addLink(link.getHyperLink(), link.getOffset(), link.getLength());
502                 }
503             }
504         }
505     }
506     
507     public ConsoleDocumentPartitioner(IProcess process, IConsoleColorProvider colorProvider) {
508         fProcess= process;
509         fColorProvider = colorProvider;
510         IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
511         boolean limit = store.getBoolean(IDebugPreferenceConstants.CONSOLE_LIMIT_CONSOLE_OUTPUT);
512         if (limit) {
513             fLowWaterMark = store.getInt(IDebugPreferenceConstants.CONSOLE_LOW_WATER_MARK);
514             fHighWaterMark = store.getInt(IDebugPreferenceConstants.CONSOLE_HIGH_WATER_MARK);
515             fMaxAppendSize = fLowWaterMark;
516         } else {
517             fLowWaterMark = -1;
518             fHighWaterMark = -1;
519             fMaxAppendSize = 80000;
520         }
521     }
522     
523     /**
524      * Stops reading/polling immediately
525      */

526     public synchronized void kill() {
527         if (!fKilled) {
528             fKilled = true;
529             if (fPollingThread != null && fPollingThread.isAlive()) {
530                 fPollingThread.interrupt();
531             }
532             fPoll = false;
533             Iterator JavaDoc iter = fStreamListeners.iterator();
534             while (iter.hasNext()) {
535                 StreamListener listener = (StreamListener)iter.next();
536                 listener.disconnect();
537             }
538             DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
539         }
540     }
541
542     public synchronized void startReading() {
543         if (fPollingThread != null) {
544             // already polling
545
return;
546         }
547         Runnable JavaDoc r = new Runnable JavaDoc() {
548             public void run() {
549                 pollAndSleep();
550             }
551         };
552         fPoll = true;
553         fPollingThread = new Thread JavaDoc(r, "Console Polling Thread"); //$NON-NLS-1$
554
fPollingThread.start();
555     }
556     
557     /**
558      * Polls and sleeps until closed or the associated
559      * process terminates
560      */

561     protected void pollAndSleep() {
562         while (!fKilled && fPoll && (!isClosed() || !fQueue.isEmpty())) {
563             poll();
564             try {
565                 Thread.sleep(BASE_DELAY);
566             } catch (InterruptedException JavaDoc e) {
567             }
568         }
569     }
570     
571     /**
572      * Polls the queue for new output and updates this document
573      */

574     protected void poll() {
575         if (isAppendInProgress()) {
576             return;
577         }
578         synchronized(fQueue) {
579             StringBuffer JavaDoc buffer = null;
580             StreamEntry prev = null;
581             int processed = 0;
582             int amount = 0;
583             String JavaDoc[] lds = fDocument.getLegalLineDelimiters();
584             boolean closed= false;
585             while (!fKilled && !closed && processed < fQueue.size() && amount < fMaxAppendSize) {
586                 StreamEntry entry = (StreamEntry)fQueue.get(processed);
587                 if (entry.isClosedEntry()) {
588                     closed = true;
589                     processed++;
590                 } else if (prev == null || prev.getStreamIdentifier().equals(entry.getStreamIdentifier())) {
591                     String JavaDoc text = entry.getText();
592                     if (buffer == null) {
593                         buffer = new StringBuffer JavaDoc(text.length());
594                     }
595                     if (isWrap()) {
596                         for (int i = 0; i < text.length(); i++) {
597                             if (fLineLength >= fMaxLineLength) {
598                                 String JavaDoc d = getLineDelimiter(text, i, lds);
599                                 if (d == null) {
600                                     buffer.append(lds[0]);
601                                 } else {
602                                     buffer.append(d);
603                                     i = i + d.length();
604                                 }
605                                 fLineLength = 0;
606                             }
607                             if (i < text.length()) {
608                                 String JavaDoc lineDelimiter = getLineDelimiter(text, i, lds);
609                                 if (lineDelimiter == null) {
610                                     buffer.append(text.charAt(i));
611                                     fLineLength++;
612                                 } else {
613                                     buffer.append(lineDelimiter);
614                                     fLineLength = 0;
615                                     i = i + lineDelimiter.length() - 1;
616                                 }
617                             }
618                         }
619                     } else {
620                         buffer.append(text);
621                     }
622                     prev = entry;
623                     processed++;
624                     amount+= entry.getText().length();
625                 } else {
626                     // change streams - write the contents of the current stream
627
// and start processing the next stream
628
if (buffer != null) {
629                         appendToDocument(buffer.toString(), prev.getStreamIdentifier());
630                         buffer.setLength(0);
631                         prev = null;
632                     }
633                 }
634             }
635             if (buffer != null) {
636                 appendToDocument(buffer.toString(), prev.getStreamIdentifier());
637             }
638             if (closed) {
639                 Display display= DebugUIPlugin.getStandardDisplay();
640                 if (display != null) {
641                     display.asyncExec(new Runnable JavaDoc() {
642                         public void run() {
643                             if (fLineNotifier != null) {
644                                 fLineNotifier.streamsClosed();
645                             }
646                         }
647                     });
648                 }
649             }
650             for (int i = 0; i < processed; i++) {
651                 fQueue.remove(0);
652             }
653         }
654     }
655
656     /**
657      * Returns the longest line delimiter at the given position in the given text,
658      * or <code>null</code> if none.
659      *
660      * @param text the text in which to look for a line delimiter
661      * @param pos the position at which to look for a line delimiter
662      * @param lineDelimiters the line delimiters to look for
663      */

664     protected String JavaDoc getLineDelimiter(String JavaDoc text, int pos, String JavaDoc[] lineDelimiters) {
665         String JavaDoc ld = null;
666         for (int i = 0; i < lineDelimiters.length; i++) {
667             if (text.regionMatches(pos, lineDelimiters[i], 0, lineDelimiters[i].length())) {
668                 if (ld == null) {
669                     ld = lineDelimiters[i];
670                 } else {
671                     if (ld.length() < lineDelimiters[i].length()) {
672                         ld = lineDelimiters[i];
673                     }
674                 }
675             }
676         }
677         return ld;
678     }
679     
680     /**
681      * Returns whether this console document is performing auto-wrap
682      */

683     protected boolean isWrap() {
684         return fWrap;
685     }
686     
687     /**
688      * The stream with the given identifier has had text appended to it.
689      * Adds the new text to the document.
690      *
691      * @see IStreamListener#streamAppended(String, IStreamMonitor)
692      */

693     protected void appendToDocument(final String JavaDoc text, final String JavaDoc streamIdentifier) {
694         Runnable JavaDoc r = new Runnable JavaDoc() {
695             public void run() {
696                 setAppendInProgress(true);
697                 fLastStreamIdentifier = streamIdentifier;
698                 try {
699                     fDocument.replace(fDocument.getLength(), 0, text);
700                     warnOfContentChange();
701                 } catch (BadLocationException e) {
702                 }
703                 setAppendInProgress(false);
704                 checkOverflow();
705             }
706         };
707         Display display = DebugUIPlugin.getStandardDisplay();
708         if (display != null) {
709             display.asyncExec(r);
710         }
711     }
712     
713     /**
714      * Checks to see if the console buffer has overflowed, and empties the
715      * overflow if needed, updating partitions and hyperlink positions.
716      */

717     protected void checkOverflow() {
718         if (fHighWaterMark >= 0) {
719             if (fDocument.getLength() > fHighWaterMark) {
720                 int lineDifference = 0;
721                 if (fLineNotifier != null) {
722                     int processed = fLineNotifier.getLinesProcessed();
723                     int numLines = fDocument.getNumberOfLines();
724                     lineDifference = numLines - processed;
725                 }
726                 int overflow = fDocument.getLength() - fLowWaterMark;
727                 fUpdatingBuffer = true;
728                 try {
729                     // update partitions
730
List JavaDoc newParitions = new ArrayList JavaDoc(fPartitions.size());
731                     Iterator JavaDoc partitions = fPartitions.iterator();
732                     while (partitions.hasNext()) {
733                         ITypedRegion region = (ITypedRegion)partitions.next();
734                         if (region instanceof StreamPartition) {
735                             StreamPartition streamPartition = (StreamPartition)region;
736                             ITypedRegion newPartition = null;
737                             int offset = region.getOffset();
738                             if (offset < overflow) {
739                                 int endOffset = offset + region.getLength();
740                                 if (endOffset < overflow) {
741                                     // remove partition
742
} else {
743                                     // split partition
744
int length = endOffset - overflow;
745                                     newPartition = streamPartition.createNewPartition(streamPartition.getStreamIdentifier(), 0, length);
746                                 }
747                             } else {
748                                 // modify parition offset
749
newPartition = streamPartition.createNewPartition(streamPartition.getStreamIdentifier(), streamPartition.getOffset() - overflow, streamPartition.getLength());
750                             }
751                             if (newPartition != null) {
752                                 newParitions.add(newPartition);
753                             }
754                         }
755                     }
756                     fPartitions = newParitions;
757                     // update hyperlinks
758
try {
759                         Position[] hyperlinks = fDocument.getPositions(HyperlinkPosition.HYPER_LINK_CATEGORY);
760                         for (int i = 0; i < hyperlinks.length; i++) {
761                             HyperlinkPosition position = (HyperlinkPosition)hyperlinks[i];
762                             // remove old the position
763
fDocument.removePosition(HyperlinkPosition.HYPER_LINK_CATEGORY, position);
764                             if (position.getOffset() >= overflow) {
765                                 // add new poisition
766
try {
767                                     fDocument.addPosition(HyperlinkPosition.HYPER_LINK_CATEGORY, new HyperlinkPosition(position.getHyperLink(), position.getOffset() - overflow, position.getLength()));
768                                 } catch (BadLocationException e) {
769                                 }
770                             }
771                         }
772                     } catch (BadPositionCategoryException e) {
773                     }
774                     synchronized (fPendingLinks) {
775                         // update pending hyperlinks
776
Vector JavaDoc newPendingLinks = new Vector JavaDoc(fPendingLinks.size());
777                         Iterator JavaDoc pendingLinks = fPendingLinks.iterator();
778                         while (pendingLinks.hasNext()) {
779                             HyperlinkPosition position = (HyperlinkPosition)pendingLinks.next();
780                             if (position.getOffset() >= overflow) {
781                                 newPendingLinks.add(new HyperlinkPosition(position.getHyperLink(), position.getOffset() - overflow, position.getLength()));
782                             }
783                         }
784                         fPendingLinks = newPendingLinks;
785                     }
786                     
787                     // remove overflow text
788
try {
789                         fDocument.replace(0, overflow, ""); //$NON-NLS-1$
790
} catch (BadLocationException e) {
791                         DebugUIPlugin.log(e);
792                     }
793                 } finally {
794                     // update number of lines processed
795
if (fLineNotifier != null) {
796                         fLineNotifier.setLinesProcessed(fDocument.getNumberOfLines() - lineDifference);
797                     }
798                     fUpdatingBuffer = false;
799                 }
800             }
801         }
802     }
803     
804     /**
805      * The stream with the given identifier has had text appended to it.
806      * Adds a new entry to the queue.
807      */

808     protected void streamAppended(String JavaDoc text, String JavaDoc streamIdentifier) {
809         synchronized (fQueue) {
810             if (fClosed) {
811                 // ERROR - attempt to append after console is closed
812
DebugUIPlugin.logErrorMessage("An attempt was made to append text to the console, after it was closed."); //$NON-NLS-1$
813
} else {
814                 fQueue.add(new StreamEntry(text, streamIdentifier));
815             }
816         }
817     }
818     
819     /**
820      * The streams associated with this process have been closed.
821      * Adds a new "stream closed" entry to the queue.
822      */

823     protected void streamsClosed() {
824         synchronized (fQueue) {
825             if (!fClosed) {
826                 fQueue.add(new StreamsClosedEntry());
827                 fClosed = true;
828             }
829         }
830     }
831                     
832     /**
833      * Sets whether a runnable has been submitted to update the console
834      * document.
835      */

836     protected void setAppendInProgress(boolean appending) {
837         fAppending = appending;
838     }
839
840     /**
841      * Returns whether a runnable has been submitted to update the console
842      * document.
843      */

844     protected boolean isAppendInProgress() {
845         return fAppending;
846     }
847     
848     /**
849      * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
850      */

851     public void propertyChange(PropertyChangeEvent event) {
852         if (event.getProperty().equals(IDebugPreferenceConstants.CONSOLE_WRAP)) {
853             fWrap = DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.CONSOLE_WRAP);
854         } else if (event.getProperty().equals(IDebugPreferenceConstants.CONSOLE_WIDTH)) {
855             fMaxLineLength = DebugUIPlugin.getDefault().getPreferenceStore().getInt(IDebugPreferenceConstants.CONSOLE_WIDTH);
856         }
857     }
858     
859     /**
860      * Returns a collection of legal line delimiters for this partitioner's
861      * associated document, sorted by length in descending order.
862      */

863     protected String JavaDoc[] getLegalLineDelimiters() {
864         if (fSortedLineDelimiters == null) {
865             String JavaDoc[] lineDelimiters = fDocument.getLegalLineDelimiters();
866             List JavaDoc list = new ArrayList JavaDoc(lineDelimiters.length);
867             for (int i = 0; i < lineDelimiters.length; i++) {
868                 list.add(lineDelimiters[i]);
869             }
870             Comparator JavaDoc comparator = new Comparator JavaDoc() {
871                 /**
872                  * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
873                  */

874                 public int compare(Object JavaDoc a, Object JavaDoc b) {
875                     String JavaDoc s1 = (String JavaDoc)a;
876                     String JavaDoc s2 = (String JavaDoc)b;
877                     return s2.length() - s1.length();
878                 }
879     
880             };
881             Collections.sort(list, comparator);
882             fSortedLineDelimiters = (String JavaDoc[])list.toArray(new String JavaDoc[lineDelimiters.length]);
883         }
884         return fSortedLineDelimiters;
885     }
886
887     /* (non-Javadoc)
888      * @see org.eclipse.debug.ui.console.IConsole#connect(org.eclipse.debug.core.model.IStreamMonitor, java.lang.String)
889      */

890     public void connect(IStreamMonitor streamMonitor, String JavaDoc streamIdentifer) {
891         if (streamMonitor != null) {
892             StreamListener listener = new StreamListener(streamIdentifer, streamMonitor);
893             fStreamListeners.add(listener);
894             listener.connect();
895             // ensure we start polling for output
896
startReading();
897         }
898     }
899
900     /* (non-Javadoc)
901      * @see org.eclipse.debug.ui.console.IConsole#connect(org.eclipse.debug.core.model.IStreamsProxy)
902      */

903     public void connect(IStreamsProxy streamsProxy) {
904         fProxy = streamsProxy;
905         connect(streamsProxy.getOutputStreamMonitor(), IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM);
906         connect(streamsProxy.getErrorStreamMonitor(), IDebugUIConstants.ID_STANDARD_ERROR_STREAM);
907     }
908     
909     /**
910      * Returns whether the streams assocaited with this console's process
911      * have been closed.
912      */

913     protected boolean isClosed() {
914         return fClosed;
915     }
916
917     protected IConsoleColorProvider getColorProvider() {
918         return fColorProvider;
919     }
920
921     /* (non-Javadoc)
922      * @see org.eclipse.debug.ui.console.IConsole#addLink(org.eclipse.debug.ui.console.IConsoleHyperlink, int, int)
923      */

924     public void addLink(IConsoleHyperlink link, int offset, int length) {
925         HyperlinkPosition hyperlinkPosition = new HyperlinkPosition(link, offset, length);
926         try {
927             fDocument.addPosition(HyperlinkPosition.HYPER_LINK_CATEGORY, hyperlinkPosition);
928         } catch (BadPositionCategoryException e) {
929             // internal error
930
DebugUIPlugin.log(e);
931         } catch (BadLocationException e) {
932             // queue the link
933
fPendingLinks.add(hyperlinkPosition);
934         }
935     }
936
937     /* (non-Javadoc)
938      * @see org.eclipse.debug.ui.console.IConsole#getDocument()
939      */

940     public IDocument getDocument() {
941         return fDocument;
942     }
943
944     /* (non-Javadoc)
945      * @see org.eclipse.debug.ui.console.IConsole#getProcess()
946      */

947     public IProcess getProcess() {
948         return fProcess;
949     }
950     
951     /**
952      * Connects the given line notifier to this console document partitioner
953      *
954      * @param lineNotifier
955      */

956     public void connectLineNotifier(ConsoleLineNotifier lineNotifier) {
957         fLineNotifier = lineNotifier;
958         lineNotifier.connect(this);
959     }
960
961     /* (non-Javadoc)
962      * @see org.eclipse.debug.ui.console.IConsole#getRegion(org.eclipse.debug.ui.console.IConsoleHyperlink)
963      */

964     public IRegion getRegion(IConsoleHyperlink link) {
965         try {
966             Position[] positions = getDocument().getPositions(HyperlinkPosition.HYPER_LINK_CATEGORY);
967             for (int i = 0; i < positions.length; i++) {
968                 HyperlinkPosition position = (HyperlinkPosition)positions[i];
969                 if (position.getHyperLink().equals(link)) {
970                     return new Region(position.getOffset(), position.getLength());
971                 }
972             }
973         } catch (BadPositionCategoryException e) {
974         }
975         return null;
976     }
977     /* (non-Javadoc)
978      * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
979      */

980     public void handleDebugEvents(DebugEvent[] events) {
981         for (int i = 0; i < events.length; i++) {
982             DebugEvent event = events[i];
983             if (event.getKind() == DebugEvent.TERMINATE && event.getSource().equals(getProcess())) {
984                 DebugPlugin.getDefault().removeDebugEventListener(this);
985                 streamsClosed();
986             }
987         }
988
989     }
990
991     private void warnOfContentChange() {
992         ConsolePlugin.getDefault().getConsoleManager().warnOfContentChange(DebugUITools.getConsole(fProcess));
993     }
994
995 }
996
Popular Tags