KickJava   Java API By Example, From Geeks To Geeks.

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


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.ui.internal.console;
13
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19 import org.eclipse.jface.text.BadLocationException;
20 import org.eclipse.jface.text.Document;
21 import org.eclipse.jface.text.DocumentEvent;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.IDocumentPartitioner;
24 import org.eclipse.jface.text.IDocumentPartitionerExtension;
25 import org.eclipse.jface.text.IRegion;
26 import org.eclipse.jface.text.ITypedRegion;
27 import org.eclipse.jface.text.Region;
28 import org.eclipse.swt.widgets.Display;
29 import org.eclipse.ui.console.ConsolePlugin;
30 import org.eclipse.ui.console.IConsoleManager;
31 import org.eclipse.ui.console.MessageConsoleStream;
32
33 /**
34  * A console that displays text messages.
35  *
36  * @since 3.0
37  */

38 public class MessageConsolePartitioner implements IDocumentPartitioner, IDocumentPartitionerExtension {
39     
40     /**
41      * The associated docuemnt
42      */

43     private IDocument fDocument = null;
44             
45     /**
46      * List of partitions
47      */

48     private List JavaDoc fPartitions = new ArrayList JavaDoc(5);
49         
50     /**
51      * The stream that was last appended to
52      */

53     private MessageConsoleStream fLastStream = null;
54     
55     
56     private int highWaterMark = 100000;
57     private int lowWaterMark = 80000;
58     private int maxAppendSize = lowWaterMark;
59
60     private List JavaDoc streamEntries = new ArrayList JavaDoc();
61     private boolean killed = false;
62     private boolean updaterThreadStarted = false;
63
64     private IConsoleManager fConsoleManager;
65     /**
66      * Creates a new paritioner and document, and connects this partitioner
67      * to the document.
68      */

69     public MessageConsolePartitioner() {
70         IDocument doc = new Document();
71         connect(doc);
72     }
73     
74     /**
75      * Sets the low and high water marks for this console's text buffer.
76      *
77      * @param low low water mark
78      * @param high high water mark
79      */

80     public void setWaterMarks(int low, int high) {
81         if (low >= high) {
82             throw new IllegalArgumentException JavaDoc(ConsoleMessages.getString("MessageConsolePartitioner.2")); //$NON-NLS-1$
83
}
84         if (low < 1000) {
85             throw new IllegalArgumentException JavaDoc(ConsoleMessages.getString("MessageConsolePartitioner.3")); //$NON-NLS-1$
86
}
87         lowWaterMark = low;
88         highWaterMark = high;
89         maxAppendSize = Math.min(80000, low);
90     }
91     /**
92      * @return Returns the highWaterMark.
93      */

94     public int getHighWaterMark() {
95         return highWaterMark;
96     }
97
98     /**
99      * @return Returns the lowWaterMark.
100      */

101     public int getLowWaterMark() {
102         return lowWaterMark;
103     }
104
105     /**
106      * @return Returns the maxAppendSize.
107      */

108     public int getMaxAppendSize() {
109         return maxAppendSize;
110     }
111
112     /**
113      * @param maxAppendSize The maxAppendSize to set.
114      */

115     public void setMaxAppendSize(int maxAppendSize) {
116         this.maxAppendSize = maxAppendSize;
117     }
118     
119     
120     /**
121      * @see org.eclipse.jface.text.IDocumentPartitioner#connect(org.eclipse.jface.text.IDocument)
122      */

123     public void connect(IDocument document) {
124         fDocument = document;
125         document.setDocumentPartitioner(this);
126         fConsoleManager = ConsolePlugin.getDefault().getConsoleManager();
127     }
128
129     /**
130      * @see org.eclipse.jface.text.IDocumentPartitioner#disconnect()
131      */

132     public void disconnect() {
133         fDocument.setDocumentPartitioner(null);
134         killed = true;
135         fConsoleManager = null;
136     }
137
138     /**
139      * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
140      */

141     public void documentAboutToBeChanged(DocumentEvent event) {
142     }
143
144     /**
145      * @see org.eclipse.jface.text.IDocumentPartitioner#documentChanged(org.eclipse.jface.text.DocumentEvent)
146      */

147     public boolean documentChanged(DocumentEvent event) {
148         return documentChanged2(event) != null;
149     }
150
151     /**
152      * @see org.eclipse.jface.text.IDocumentPartitioner#getLegalContentTypes()
153      */

154     public String JavaDoc[] getLegalContentTypes() {
155         return new String JavaDoc[] {MessageConsolePartition.MESSAGE_PARTITION_TYPE};
156     }
157
158     /**
159      * @see org.eclipse.jface.text.IDocumentPartitioner#getContentType(int)
160      */

161     public String JavaDoc getContentType(int offset) {
162         ITypedRegion partition = getPartition(offset);
163         if (partition != null) {
164             return partition.getType();
165         }
166         return null;
167     }
168
169     /**
170      * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
171      */

172     public ITypedRegion[] computePartitioning(int offset, int length) {
173         if (offset == 0 && length == fDocument.getLength()) {
174             return (ITypedRegion[])fPartitions.toArray(new ITypedRegion[fPartitions.size()]);
175         }
176         int end = offset + length;
177         List JavaDoc list = new ArrayList JavaDoc();
178         for (int i = 0; i < fPartitions.size(); i++) {
179             ITypedRegion partition = (ITypedRegion)fPartitions.get(i);
180             int partitionStart = partition.getOffset();
181             int partitionEnd = partitionStart + partition.getLength();
182             if ((offset >= partitionStart && offset <= partitionEnd) ||
183                 (offset < partitionStart && end >= partitionStart)) {
184                     list.add(partition);
185             }
186         }
187         return (ITypedRegion[])list.toArray(new ITypedRegion[list.size()]);
188     }
189
190     /**
191      * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
192      */

193     public ITypedRegion getPartition(int offset) {
194         for (int i = 0; i < fPartitions.size(); i++) {
195             ITypedRegion partition = (ITypedRegion)fPartitions.get(i);
196             int start = partition.getOffset();
197             int end = start + partition.getLength();
198             if (offset >= start && offset < end) {
199                 return partition;
200             }
201         }
202         return null;
203     }
204
205     /**
206      * @see org.eclipse.jface.text.IDocumentPartitionerExtension#documentChanged2(org.eclipse.jface.text.DocumentEvent)
207      */

208     public IRegion documentChanged2(DocumentEvent event) {
209         
210         String JavaDoc text = event.getText();
211         if (getDocument().getLength() == 0) {
212             // cleared
213
fPartitions.clear();
214             return new Region(0,0);
215         }
216         addPartition(new MessageConsolePartition(fLastStream, event.getOffset(), text.length()));
217         ITypedRegion[] affectedRegions = computePartitioning(event.getOffset(), text.length());
218         if (affectedRegions.length == 0) {
219             return null;
220         }
221         if (affectedRegions.length == 1) {
222             return affectedRegions[0];
223         }
224         int affectedLength = affectedRegions[0].getLength();
225         for (int i = 1; i < affectedRegions.length; i++) {
226             ITypedRegion region = affectedRegions[i];
227             affectedLength += region.getLength();
228         }
229         
230         return new Region(affectedRegions[0].getOffset(), affectedLength);
231     }
232
233
234     /**
235      * Checks to see if the console buffer has overflowed, and empties the
236      * overflow if needed, updating partitions and hyperlink positions.
237      */

238     protected void checkOverflow() {
239         if (highWaterMark >= 0) {
240             if (fDocument.getLength() > highWaterMark) {
241                 int overflow = fDocument.getLength() - lowWaterMark;
242                 
243                 try {
244                     int line = fDocument.getLineOfOffset(overflow);
245                     int nextLineOffset = fDocument.getLineOffset(line+1);
246                     overflow = nextLineOffset;
247                 } catch (BadLocationException e1) {
248                 }
249                 
250                 // update partitions
251
List JavaDoc newParitions = new ArrayList JavaDoc(fPartitions.size());
252                 Iterator JavaDoc partitions = fPartitions.iterator();
253                 while (partitions.hasNext()) {
254                     ITypedRegion region = (ITypedRegion) partitions.next();
255                     if (region instanceof MessageConsolePartition) {
256                         MessageConsolePartition messageConsolePartition = (MessageConsolePartition)region;
257
258                         ITypedRegion newPartition = null;
259                         int offset = region.getOffset();
260                         if (offset < overflow) {
261                             int endOffset = offset + region.getLength();
262                             if (endOffset < overflow) {
263                                 // remove partition
264
} else {
265                                 // split partition
266
int length = endOffset - overflow;
267                                 newPartition = messageConsolePartition.createNewPartition(0, length);
268                             }
269                         } else {
270                             // modify parition offset
271
newPartition = messageConsolePartition.createNewPartition(messageConsolePartition.getOffset()-overflow, messageConsolePartition.getLength());
272                         }
273                         if (newPartition != null) {
274                             newParitions.add(newPartition);
275                         }
276                     }
277                 }
278                 fPartitions = newParitions;
279         
280                 //called from GUI Thread (see startUpdaterThread()), no asyncExec needed.
281
try {
282                     fDocument.replace(0, overflow, ""); //$NON-NLS-1$
283
} catch (BadLocationException e) {
284                 }
285             }
286         }
287     }
288         
289
290     /**
291      * Adds a new colored input partition, combining with the previous partition if
292      * possible.
293      */

294     private MessageConsolePartition addPartition(MessageConsolePartition partition) {
295         if (fPartitions.isEmpty()) {
296             fPartitions.add(partition);
297         } else {
298             int index = fPartitions.size() - 1;
299             MessageConsolePartition last = (MessageConsolePartition)fPartitions.get(index);
300             if (last.canBeCombinedWith(partition)) {
301                 // replace with a single partition
302
partition = last.combineWith(partition);
303                 fPartitions.set(index, partition);
304             } else {
305                 // different kinds - add a new parition
306
fPartitions.add(partition);
307             }
308         }
309         return partition;
310     }
311     
312     /**
313      * Returns the document this partitioner is connected to, or <code>null</code>
314      * if none.
315      *
316      * @return the document this partitioner is connected to, or <code>null</code>
317      * if none
318      */

319     public IDocument getDocument() {
320         return fDocument;
321     }
322
323     /**
324      *
325      */

326     private void startUpdaterThread() {
327         if (updaterThreadStarted) {
328             return;
329         }
330         
331         updaterThreadStarted = true;
332         
333         Runnable JavaDoc r = new Runnable JavaDoc() {
334             public void run() {
335
336                 while(!killed && streamEntries.size()>0) {
337                     synchronized(streamEntries) {
338                         final StreamEntry streamEntry = (StreamEntry)streamEntries.get(0);
339                         streamEntries.remove(0);
340                          
341                         Runnable JavaDoc innerRunnable = new Runnable JavaDoc() {
342                             public void run() {
343                                 fLastStream = streamEntry.stream;
344                                 try {
345                                     fDocument.replace(fDocument.getLength(), 0, streamEntry.text.toString());
346                                     checkOverflow();
347                                     fConsoleManager.warnOfContentChange(streamEntry.stream.getConsole());
348                                 } catch (BadLocationException e) {
349                                 }
350                             }
351                         };
352                         Display display = ConsolePlugin.getStandardDisplay();
353                         if (display != null) {
354                             display.asyncExec(innerRunnable);
355                         }
356                         
357                         try {
358                             //Don't just die! Give up the lock and allow more StreamEntry objects to be
359
//added to list
360
Thread.sleep(100);
361                         } catch (InterruptedException JavaDoc e) {
362                         }
363                     }
364                 }
365                 updaterThreadStarted = false;
366             }
367         };
368         
369         new Thread JavaDoc(r, "MessageConsoleUpdaterThread").start(); //$NON-NLS-1$
370
}
371     
372     /**
373      * Adds the new text to the document.
374      *
375      * @param text the text to append
376      * @param stream the stream to append to
377      */

378     public void appendToDocument(final String JavaDoc text, final MessageConsoleStream stream) {
379         int offset = 0;
380         int length = text.length();
381         
382         synchronized(streamEntries) {
383             //try to fit in last StreamEntry if they are the same stream
384
if (streamEntries.size() > 0) {
385                 StreamEntry streamEntry = (StreamEntry)streamEntries.get(streamEntries.size()-1);
386                 if (streamEntry.stream == stream) {
387                     int emptySpace = maxAppendSize - streamEntry.text.length();
388                     if (length <= emptySpace) {
389                         streamEntry.text.append(text);
390                         offset = length;
391                         length = 0;
392                     } else {
393                         streamEntry.text.append(text.substring(offset, emptySpace));
394                         offset += emptySpace;
395                         length -= emptySpace;
396                     }
397                 }
398             }
399             
400             //put remaining text into new StreamEntry objects
401
while (length > 0) {
402                 int toCopy = Math.min(maxAppendSize, length);
403                 String JavaDoc substring = text.substring(offset, offset+toCopy);
404                 StreamEntry streamEntry = new StreamEntry(substring, stream);
405                 streamEntries.add(streamEntry);
406                 offset += toCopy;
407                 length -= toCopy;
408             }
409             
410         } //give up the lock
411

412         startUpdaterThread();
413     }
414     
415     private class StreamEntry {
416         MessageConsoleStream stream;
417         StringBuffer JavaDoc text;
418         
419         StreamEntry(String JavaDoc text, MessageConsoleStream stream) {
420             this.stream = stream;
421             this.text = new StringBuffer JavaDoc(text);
422         }
423     }
424 }
Popular Tags