KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > console > IOConsolePartitioner


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

11 package org.eclipse.ui.internal.console;
12
13 import java.io.IOException JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.OperationCanceledException;
21 import org.eclipse.core.runtime.Status;
22 import org.eclipse.core.runtime.jobs.IJobManager;
23 import org.eclipse.core.runtime.jobs.Job;
24 import org.eclipse.jface.text.BadLocationException;
25 import org.eclipse.jface.text.DocumentEvent;
26 import org.eclipse.jface.text.IDocument;
27 import org.eclipse.jface.text.IDocumentPartitionerExtension;
28 import org.eclipse.jface.text.IRegion;
29 import org.eclipse.jface.text.ITypedRegion;
30 import org.eclipse.jface.text.Region;
31 import org.eclipse.swt.custom.StyleRange;
32 import org.eclipse.ui.console.ConsolePlugin;
33 import org.eclipse.ui.console.IConsoleDocumentPartitioner;
34 import org.eclipse.ui.console.IOConsole;
35 import org.eclipse.ui.console.IOConsoleInputStream;
36 import org.eclipse.ui.console.IOConsoleOutputStream;
37 import org.eclipse.ui.progress.UIJob;
38 import org.eclipse.ui.progress.WorkbenchJob;
39
40 /**
41  * Partitions an IOConsole's document
42  * @since 3.1
43  *
44  */

45 public class IOConsolePartitioner implements IConsoleDocumentPartitioner, IDocumentPartitionerExtension {
46     private PendingPartition consoleClosedPartition;
47     private IDocument document;
48     private ArrayList JavaDoc partitions;
49     /**
50      * Blocks of data that have not yet been appended to the document.
51      */

52     private ArrayList JavaDoc pendingPartitions;
53     /**
54      * A list of PendingPartitions to be appended by the updateJob
55      */

56     private ArrayList JavaDoc updatePartitions;
57     /**
58      * The last partition appended to the document
59      */

60     private IOConsolePartition lastPartition;
61     /**
62      * Job that appends pending partitions to the document.
63      */

64     private QueueProcessingJob queueJob;
65     /**
66      * The input stream attached to this document.
67      */

68     private IOConsoleInputStream inputStream;
69     /**
70      * Flag to indicate that the updateJob is updating the document.
71      */

72     private boolean updateInProgress;
73     /**
74      * A list of partitions containing input from the console, that have
75      * not been appended to the input stream yet.
76      */

77     private ArrayList JavaDoc inputPartitions;
78     /**
79      * offset used by updateJob
80      */

81     private int firstOffset;
82     /**
83      * An array of legal line delimiters
84      */

85     private String JavaDoc[] lld;
86     private int highWaterMark = -1;
87     private int lowWaterMark = -1;
88     private boolean connected = false;
89
90     private IOConsole console;
91     
92     private TrimJob trimJob = new TrimJob();
93     /**
94      * Lock for appending to and removing from the document - used
95      * to synchronize addition of new text/partitions in the update
96      * job and handling buffer overflow/clearing of the console.
97      */

98     private Object JavaDoc overflowLock = new Object JavaDoc();
99     
100     
101     private int fBuffer;
102     
103     public IOConsolePartitioner(IOConsoleInputStream inputStream, IOConsole console) {
104         this.inputStream = inputStream;
105         this.console = console;
106         trimJob.setRule(console.getSchedulingRule());
107     }
108     
109     public IDocument getDocument() {
110         return document;
111     }
112     
113     /*
114      * (non-Javadoc)
115      * @see org.eclipse.jface.text.IDocumentPartitioner#connect(org.eclipse.jface.text.IDocument)
116      */

117     public void connect(IDocument doc) {
118         document = doc;
119         document.setDocumentPartitioner(this);
120         lld = document.getLegalLineDelimiters();
121         partitions = new ArrayList JavaDoc();
122         pendingPartitions = new ArrayList JavaDoc();
123         inputPartitions = new ArrayList JavaDoc();
124         queueJob = new QueueProcessingJob();
125         queueJob.setSystem(true);
126         queueJob.setPriority(Job.INTERACTIVE);
127         queueJob.setRule(console.getSchedulingRule());
128         connected = true;
129     }
130     
131     public int getHighWaterMark() {
132         return highWaterMark;
133     }
134     
135     public int getLowWaterMark() {
136         return lowWaterMark;
137     }
138     
139     public void setWaterMarks(int low, int high) {
140         lowWaterMark = low;
141         highWaterMark = high;
142         ConsolePlugin.getStandardDisplay().asyncExec(new Runnable JavaDoc() {
143             public void run() {
144                 checkBufferSize();
145             }
146         });
147     }
148     
149     /**
150      * Notification from the console that all of its streams have been closed.
151      */

152     public void streamsClosed() {
153         consoleClosedPartition = new PendingPartition(null, null);
154         synchronized (pendingPartitions) {
155             pendingPartitions.add(consoleClosedPartition);
156         }
157         queueJob.schedule(); //ensure that all pending partitions are processed.
158
}
159     
160     /*
161      * (non-Javadoc)
162      * @see org.eclipse.jface.text.IDocumentPartitioner#disconnect()
163      */

164     public void disconnect() {
165         synchronized (overflowLock) {
166             document = null;
167             partitions.clear();
168             connected = false;
169             try {
170                 inputStream.close();
171             } catch (IOException JavaDoc e) {
172             }
173         }
174     }
175     
176     /*
177      * (non-Javadoc)
178      * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
179      */

180     public void documentAboutToBeChanged(DocumentEvent event) {
181     }
182     
183     /*
184      * (non-Javadoc)
185      * @see org.eclipse.jface.text.IDocumentPartitioner#documentChanged(org.eclipse.jface.text.DocumentEvent)
186      */

187     public boolean documentChanged(DocumentEvent event) {
188         return documentChanged2(event) != null;
189     }
190     
191     /*
192      * (non-Javadoc)
193      * @see org.eclipse.jface.text.IDocumentPartitioner#getLegalContentTypes()
194      */

195     public String JavaDoc[] getLegalContentTypes() {
196         return new String JavaDoc[] { IOConsolePartition.OUTPUT_PARTITION_TYPE, IOConsolePartition.INPUT_PARTITION_TYPE };
197     }
198     
199     /*
200      * (non-Javadoc)
201      * @see org.eclipse.jface.text.IDocumentPartitioner#getContentType(int)
202      */

203     public String JavaDoc getContentType(int offset) {
204         return getPartition(offset).getType();
205     }
206     
207     /*
208      * (non-Javadoc)
209      * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
210      */

211     public ITypedRegion[] computePartitioning(int offset, int length) {
212         int rangeEnd = offset + length;
213         int left= 0;
214         int right= partitions.size() - 1;
215         int mid= 0;
216         IOConsolePartition position= null;
217         
218         if (left == right) {
219             return new IOConsolePartition[]{(IOConsolePartition) partitions.get(0)};
220         }
221         while (left < right) {
222             
223             mid= (left + right) / 2;
224                 
225             position= (IOConsolePartition) partitions.get(mid);
226             if (rangeEnd < position.getOffset()) {
227                 if (left == mid)
228                     right= left;
229                 else
230                     right= mid -1;
231             } else if (offset > (position.getOffset() + position.getLength() - 1)) {
232                 if (right == mid)
233                     left= right;
234                 else
235                     left= mid +1;
236             } else {
237                 left= right= mid;
238             }
239         }
240         
241         
242         List JavaDoc list = new ArrayList JavaDoc();
243         int index = left - 1;
244         if (index >= 0) {
245             position= (IOConsolePartition) partitions.get(index);
246             while (index >= 0 && (position.getOffset() + position.getLength()) > offset) {
247                 index--;
248                 if (index >= 0) {
249                     position= (IOConsolePartition) partitions.get(index);
250                 }
251             }
252         }
253         index++;
254         position= (IOConsolePartition) partitions.get(index);
255         while (index < partitions.size() && (position.getOffset() < rangeEnd)) {
256             list.add(position);
257             index++;
258             if (index < partitions.size()) {
259                 position= (IOConsolePartition) partitions.get(index);
260             }
261         }
262         
263         return (ITypedRegion[]) list.toArray(new IOConsolePartition[list.size()]);
264     }
265
266     
267     /*
268      * (non-Javadoc)
269      * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
270      */

271     public ITypedRegion getPartition(int offset) {
272         for (int i = 0; i < partitions.size(); i++) {
273             ITypedRegion partition = (ITypedRegion) partitions.get(i);
274             int start = partition.getOffset();
275             int end = start + partition.getLength();
276             if (offset >= start && offset < end) {
277                 return partition;
278             }
279         }
280         
281         if (lastPartition == null) {
282             synchronized(partitions) {
283                 lastPartition = new IOConsolePartition(inputStream, ""); //$NON-NLS-1$
284
lastPartition.setOffset(offset);
285                 partitions.add(lastPartition);
286                 inputPartitions.add(lastPartition);
287             }
288         }
289         return lastPartition;
290     }
291         
292     /**
293      * Enforces the buffer size.
294      * When the number of lines in the document exceeds the high water mark, the
295      * beginning of the document is trimmed until the number of lines equals the
296      * low water mark.
297      */

298     private void checkBufferSize() {
299         if (document != null && highWaterMark > 0) {
300             int length = document.getLength();
301             if (length > highWaterMark) {
302                 if (trimJob.getState() == Job.NONE) { //if the job isn't already running
303
trimJob.setOffset(length - lowWaterMark);
304                     trimJob.schedule();
305                 }
306             }
307         }
308     }
309     
310     /**
311      * Clears the console
312      */

313     public void clearBuffer() {
314         synchronized (overflowLock) {
315             trimJob.setOffset(-1);
316             trimJob.schedule();
317         }
318     }
319     
320     /*
321      * (non-Javadoc)
322      *
323      * @see org.eclipse.jface.text.IDocumentPartitionerExtension#documentChanged2(org.eclipse.jface.text.DocumentEvent)
324      */

325     public IRegion documentChanged2(DocumentEvent event) {
326         if (document == null) {
327             return null; //another thread disconnected the partitioner
328
}
329         if (document.getLength() == 0) { //document cleared
330
partitions.clear();
331             inputPartitions.clear();
332             pendingPartitions.clear();
333             lastPartition = null;
334             return new Region(0, 0);
335         }
336         
337         
338         if (updateInProgress) {
339             synchronized(partitions) {
340                 if (updatePartitions != null) {
341                     for (Iterator JavaDoc i = updatePartitions.iterator(); i.hasNext(); ) {
342                         PendingPartition pp = (PendingPartition) i.next();
343                         if (pp == consoleClosedPartition) {
344                             continue;
345                         }
346                         
347                         int ppLen = pp.text.length();
348                         if (lastPartition != null && lastPartition.getStream() == pp.stream) {
349                             int len = lastPartition.getLength();
350                             lastPartition.setLength(len + ppLen);
351                         } else {
352                             IOConsolePartition partition = new IOConsolePartition(pp.stream, ppLen);
353                             partition.setOffset(firstOffset);
354                             lastPartition = partition;
355                             partitions.add(partition);
356                         }
357                         firstOffset += ppLen;
358                     }
359                 }
360             }
361         } else {// user input.
362
int amountDeleted = event.getLength() ;
363             
364             if (amountDeleted > 0) {
365                 int offset = event.fOffset;
366                 IOConsolePartition partition = (IOConsolePartition) getPartition(offset);
367                 if(partition == lastPartition) {
368                     partition.delete(event.fOffset-partition.getOffset(), amountDeleted);
369                 }
370             }
371             
372             synchronized(partitions) {
373                 if (lastPartition == null || lastPartition.isReadOnly()) {
374                     lastPartition = new IOConsolePartition(inputStream, event.fText);
375                     lastPartition.setOffset(event.fOffset);
376                     partitions.add(lastPartition);
377                     inputPartitions.add(lastPartition);
378                 } else {
379                     lastPartition.insert(event.fText, (event.fOffset-lastPartition.getOffset()));
380                 }
381                 
382                 int lastLineDelimiter = -1;
383                 String JavaDoc partitionText = lastPartition.getString();
384                 for (int i = 0; i < lld.length; i++) {
385                     String JavaDoc ld = lld[i];
386                     int index = partitionText.lastIndexOf(ld);
387                     if (index != -1) {
388                         index += ld.length();
389                     }
390                     if (index > lastLineDelimiter) {
391                         lastLineDelimiter = index;
392                     }
393                 }
394                 if (lastLineDelimiter != -1) {
395                     StringBuffer JavaDoc input = new StringBuffer JavaDoc();
396                     Iterator JavaDoc it = inputPartitions.iterator();
397                     while (it.hasNext()) {
398                         IOConsolePartition partition = (IOConsolePartition) it.next();
399                         if (partition.getOffset() + partition.getLength() <= event.fOffset + lastLineDelimiter) {
400                             if (partition == lastPartition) {
401                                 lastPartition = null;
402                             }
403                             input.append(partition.getString());
404                             partition.clearBuffer();
405                             partition.setReadOnly();
406                             it.remove();
407                         } else {
408                             //create a new partition containing everything up to the line delimiter
409
//and append that to the string buffer.
410
String JavaDoc contentBefore = partitionText.substring(0, lastLineDelimiter);
411                             IOConsolePartition newPartition = new IOConsolePartition(inputStream, contentBefore);
412                             newPartition.setOffset(partition.getOffset());
413                             newPartition.setReadOnly();
414                             newPartition.clearBuffer();
415                             int index = partitions.indexOf(partition);
416                             partitions.add(index, newPartition);
417                             input.append(contentBefore);
418                             //delete everything that has been appended to the buffer.
419
partition.delete(0, lastLineDelimiter);
420                             partition.setOffset(lastLineDelimiter + partition.getOffset());
421                             lastLineDelimiter = 0;
422                         }
423                     }
424                     if (input.length() > 0) {
425                         inputStream.appendData(input.toString());
426                     }
427
428                 }
429             }
430         }
431         
432         return new Region(event.fOffset, event.fText.length());
433     }
434     
435     private void setUpdateInProgress(boolean b) {
436         updateInProgress = b;
437     }
438         
439     /**
440      * A stream has been appended, add to pendingPartions list and schedule updateJob.
441      * updateJob is scheduled with a slight delay, this allows the console to run the job
442      * less frequently and update the document with a greater amount of data each time
443      * the job is run
444      * @param stream The stream that was written to.
445      * @param s The string that should be appended to the document.
446      */

447     public void streamAppended(IOConsoleOutputStream stream, String JavaDoc s) throws IOException JavaDoc {
448         if (document == null) {
449             throw new IOException JavaDoc("Document is closed"); //$NON-NLS-1$
450
}
451         synchronized(pendingPartitions) {
452             PendingPartition last = (PendingPartition) (pendingPartitions.size() > 0 ? pendingPartitions.get(pendingPartitions.size()-1) : null);
453             if (last != null && last.stream == stream) {
454                 last.append(s);
455             } else {
456                 pendingPartitions.add(new PendingPartition(stream, s));
457                 if (fBuffer > 1000) {
458                     queueJob.schedule();
459                 } else {
460                     queueJob.schedule(50);
461                 }
462             }
463             
464             if (fBuffer > 160000) {
465                 try {
466                     pendingPartitions.wait();
467                 } catch (InterruptedException JavaDoc e) {
468                 }
469             }
470         }
471     }
472     
473     /**
474      * Holds data until updateJob can be run and the document can be updated.
475      */

476     private class PendingPartition {
477         StringBuffer JavaDoc text = new StringBuffer JavaDoc(8192);
478         IOConsoleOutputStream stream;
479         
480         PendingPartition(IOConsoleOutputStream stream, String JavaDoc text) {
481             this.stream = stream;
482             if (text != null) {
483                 append(text);
484             }
485         }
486         
487         void append(String JavaDoc moreText) {
488             text.append(moreText);
489             fBuffer += moreText.length();
490         }
491     }
492     
493     /**
494      * Updates the document. Will append everything that is available before
495      * finishing.
496      */

497     private class QueueProcessingJob extends UIJob {
498
499         QueueProcessingJob() {
500             super("IOConsole Updater"); //$NON-NLS-1$
501
}
502         
503         /*
504          * (non-Javadoc)
505          * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor)
506          */

507         public IStatus runInUIThread(IProgressMonitor monitor) {
508             synchronized (overflowLock) {
509                 ArrayList JavaDoc pendingCopy = new ArrayList JavaDoc();
510                 StringBuffer JavaDoc buffer = null;
511                 boolean consoleClosed = false;
512                 while (pendingPartitions.size() > 0) {
513                     synchronized(pendingPartitions) {
514                         pendingCopy.addAll(pendingPartitions);
515                         pendingPartitions.clear();
516                         fBuffer = 0;
517                         pendingPartitions.notifyAll();
518                     }
519
520                     buffer = new StringBuffer JavaDoc();
521                     for (Iterator JavaDoc i = pendingCopy.iterator(); i.hasNext(); ) {
522                         PendingPartition pp = (PendingPartition) i.next();
523                         if (pp != consoleClosedPartition) {
524                             buffer.append(pp.text);
525                         } else {
526                             consoleClosed = true;
527                         }
528                     }
529                 }
530
531                 if (connected) {
532                     setUpdateInProgress(true);
533                     updatePartitions = pendingCopy;
534                     firstOffset = document.getLength();
535                     try {
536                         document.replace(firstOffset, 0, buffer.toString());
537                     } catch (BadLocationException e) {
538                     }
539                     updatePartitions = null;
540                     setUpdateInProgress(false);
541                 }
542                 if (consoleClosed) {
543                     console.partitionerFinished();
544                 }
545                 checkBufferSize();
546
547             }
548
549             return Status.OK_STATUS;
550         }
551         
552         /*
553          * Job will process as much as it can each time it's run, but it gets
554          * scheduled everytime a PendingPartition is added to the list, meaning
555          * that this job could get scheduled unnecessarily in cases of heavy output.
556          * Note however, that schedule() will only reschedule a running/scheduled Job
557          * once even if it's called many times.
558          */

559         public boolean shouldRun() {
560             boolean shouldRun = connected && pendingPartitions != null && pendingPartitions.size() > 0;
561             return shouldRun;
562         }
563     }
564
565  
566     
567     
568     
569
570     
571     /**
572      * Job to trim the console document, runs in the UI thread.
573      */

574     private class TrimJob extends WorkbenchJob {
575         
576         /**
577          * trims output up to the line containing the given offset,
578          * or all output if -1.
579          */

580         private int truncateOffset;
581         
582         /**
583          * Creates a new job to trim the buffer.
584          */

585         TrimJob() {
586             super("Trim Job"); //$NON-NLS-1$
587
setSystem(true);
588         }
589         
590         /**
591          * Sets the trim offset.
592          *
593          * @param offset trims output up to the line containing the given offset
594          */

595         public void setOffset(int offset) {
596             truncateOffset = offset;
597         }
598
599         /* (non-Javadoc)
600          * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
601          */

602         public IStatus runInUIThread(IProgressMonitor monitor) {
603             IJobManager jobManager = Job.getJobManager();
604             try {
605                 jobManager.join(console, monitor);
606             } catch (OperationCanceledException e1) {
607                 return Status.CANCEL_STATUS;
608             } catch (InterruptedException JavaDoc e1) {
609                 return Status.CANCEL_STATUS;
610             }
611             if (document == null) {
612                 return Status.OK_STATUS;
613             }
614             
615             int length = document.getLength();
616             if (truncateOffset < length) {
617                 synchronized (overflowLock) {
618                     try {
619                         if (truncateOffset < 0) {
620                             // clear
621
setUpdateInProgress(true);
622                             document.set(""); //$NON-NLS-1$
623
setUpdateInProgress(false);
624                             partitions.clear();
625                         } else {
626                             // overflow
627
int cutoffLine = document.getLineOfOffset(truncateOffset);
628                             int cutOffset = document.getLineOffset(cutoffLine);
629
630
631                             // set the new length of the first partition
632
IOConsolePartition partition = (IOConsolePartition) getPartition(cutOffset);
633                             partition.setLength(partition.getOffset() + partition.getLength() - cutOffset);
634                             
635                             setUpdateInProgress(true);
636                             document.replace(0, cutOffset, ""); //$NON-NLS-1$
637
setUpdateInProgress(false);
638                             
639                             //remove partitions and reset Partition offsets
640
int index = partitions.indexOf(partition);
641                             for (int i = 0; i < index; i++) {
642                                 partitions.remove(0);
643                             }
644                             
645                             int offset = 0;
646                             for (Iterator JavaDoc i = partitions.iterator(); i.hasNext(); ) {
647                                 IOConsolePartition p = (IOConsolePartition) i.next();
648                                 p.setOffset(offset);
649                                 offset += p.getLength();
650                             }
651                         }
652                     } catch (BadLocationException e) {
653                     }
654                 }
655             }
656             return Status.OK_STATUS;
657         }
658     }
659
660
661
662
663
664
665
666     /* (non-Javadoc)
667      * @see org.eclipse.ui.console.IConsoleDocumentPartitioner#isReadOnly(int)
668      */

669     public boolean isReadOnly(int offset) {
670         return ((IOConsolePartition)getPartition(offset)).isReadOnly();
671     }
672
673     /* (non-Javadoc)
674      * @see org.eclipse.ui.console.IConsoleDocumentPartitioner#computeStyleRange(int, int)
675      */

676     public StyleRange[] getStyleRanges(int offset, int length) {
677         if (!connected) {
678             return new StyleRange[0];
679         }
680         IOConsolePartition[] computedPartitions = (IOConsolePartition[])computePartitioning(offset, length);
681         StyleRange[] styles = new StyleRange[computedPartitions.length];
682         for (int i = 0; i < computedPartitions.length; i++) {
683             int rangeStart = Math.max(computedPartitions[i].getOffset(), offset);
684             int rangeLength = computedPartitions[i].getLength();
685             styles[i] = computedPartitions[i].getStyleRange(rangeStart, rangeLength);
686         }
687         return styles;
688     }
689     
690     
691 }
692
Popular Tags