KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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.util.ArrayList JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.regex.Matcher JavaDoc;
17 import java.util.regex.Pattern JavaDoc;
18
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.DocumentEvent;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.IDocumentAdapter;
24 import org.eclipse.jface.text.IDocumentListener;
25 import org.eclipse.swt.custom.TextChangeListener;
26 import org.eclipse.swt.custom.TextChangedEvent;
27 import org.eclipse.swt.custom.TextChangingEvent;
28
29 /**
30  * Adapts a Console's document to the viewer StyledText widget. Allows proper line
31  * wrapping of fixed width consoles without having to add line delimiters to the StyledText.
32  *
33  * By using this adapter, the offset of any character is the same in both the widget and the
34  * document.
35  *
36  * @since 3.1
37  */

38 public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListener {
39     
40     private int consoleWidth = -1;
41     private List JavaDoc textChangeListeners;
42     private IDocument document;
43     
44     int[] offsets = new int[5000];
45     int[] lengths = new int[5000];
46     private int regionCount = 1;
47     private Pattern JavaDoc pattern = Pattern.compile("$", Pattern.MULTILINE); //$NON-NLS-1$
48

49     
50     public ConsoleDocumentAdapter(int width) {
51         textChangeListeners = new ArrayList JavaDoc();
52         consoleWidth = width;
53     }
54     
55     /*
56      * repairs lines list from the beginning of the line containing the offset of any
57      * DocumentEvent, to the end of the Document.
58      */

59     private void repairLines(int eventOffset) {
60         if (document == null) {
61             return;
62         }
63         try {
64             int docLine = document.getLineOfOffset(eventOffset);
65             int docLineOffset = document.getLineOffset(docLine);
66             int widgetLine = getLineAtOffset(docLineOffset);
67             
68             for (int i=regionCount-1; i>=widgetLine; i--) {
69                 regionCount--;
70             }
71             
72             int numLinesInDoc = document.getNumberOfLines();
73
74             int nextOffset = document.getLineOffset(docLine);
75             for (int i = docLine; i<numLinesInDoc; i++) {
76                 int offset = nextOffset;
77                 int length = document.getLineLength(i);
78                 nextOffset += length;
79                 
80                 if (length == 0) {
81                     addRegion(offset, 0);
82                 } else {
83                     while (length > 0) {
84                         int trimmedLength = length;
85                         String JavaDoc lineDelimiter = document.getLineDelimiter(i);
86                         int lineDelimiterLength = 0;
87                         if (lineDelimiter != null) {
88                             lineDelimiterLength = lineDelimiter.length();
89                             trimmedLength -= lineDelimiterLength;
90                         }
91
92                         if (consoleWidth > 0 && consoleWidth < trimmedLength) {
93                             addRegion(offset, consoleWidth);
94                             offset += consoleWidth;
95                             length -= consoleWidth;
96                         } else {
97                             addRegion(offset, length);
98                             offset += length;
99                             length -= length;
100                         }
101                     }
102                 }
103             }
104         } catch (BadLocationException e) {
105         }
106         
107         if (regionCount == 0) {
108             addRegion(0, document.getLength());
109         }
110     }
111     
112     private void addRegion(int offset, int length) {
113         if (regionCount == 0) {
114             offsets[0] = offset;
115             lengths[0] = length;
116         } else {
117             if (regionCount == offsets.length) {
118                 growRegionArray(regionCount * 2);
119             }
120             offsets[regionCount] = offset;
121             lengths[regionCount] = length;
122         }
123         regionCount++;
124     }
125     
126     /* (non-Javadoc)
127      * @see org.eclipse.jface.text.IDocumentAdapter#setDocument(org.eclipse.jface.text.IDocument)
128      */

129     public void setDocument(IDocument doc) {
130         if (document != null) {
131             document.removeDocumentListener(this);
132         }
133         
134         document = doc;
135         
136         if (document != null) {
137             document.addDocumentListener(this);
138             repairLines(0);
139         }
140     }
141
142     /* (non-Javadoc)
143      * @see org.eclipse.swt.custom.StyledTextContent#addTextChangeListener(org.eclipse.swt.custom.TextChangeListener)
144      */

145     public synchronized void addTextChangeListener(TextChangeListener listener) {
146         Assert.isNotNull(listener);
147         if (!textChangeListeners.contains(listener)) {
148             textChangeListeners.add(listener);
149         }
150     }
151     
152     /* (non-Javadoc)
153      * @see org.eclipse.swt.custom.StyledTextContent#removeTextChangeListener(org.eclipse.swt.custom.TextChangeListener)
154      */

155     public synchronized void removeTextChangeListener(TextChangeListener listener) {
156         if(textChangeListeners != null) {
157             Assert.isNotNull(listener);
158             textChangeListeners.remove(listener);
159         }
160     }
161
162     /* (non-Javadoc)
163      * @see org.eclipse.swt.custom.StyledTextContent#getCharCount()
164      */

165     public int getCharCount() {
166         return document.getLength();
167     }
168
169     /* (non-Javadoc)
170      * @see org.eclipse.swt.custom.StyledTextContent#getLine(int)
171      */

172     public String JavaDoc getLine(int lineIndex) {
173         try {
174             StringBuffer JavaDoc line = new StringBuffer JavaDoc(document.get(offsets[lineIndex], lengths[lineIndex]));
175             int index = line.length() - 1;
176             while(index > -1 && (line.charAt(index)=='\n' || line.charAt(index)=='\r')) {
177                 index--;
178             }
179             return new String JavaDoc(line.substring(0, index+1));
180         } catch (BadLocationException e) {
181         }
182         return ""; //$NON-NLS-1$
183
}
184
185     /* (non-Javadoc)
186      * @see org.eclipse.swt.custom.StyledTextContent#getLineAtOffset(int)
187      */

188     public int getLineAtOffset(int offset) {
189         if (offset == 0 || regionCount <= 1) {
190             return 0;
191         }
192         
193         if (offset == document.getLength()) {
194             return regionCount-1;
195         }
196         
197         int left= 0;
198         int right= regionCount-1;
199         int midIndex = 0;
200         
201         while (left <= right) {
202             if(left == right) {
203                 return right;
204             }
205             midIndex = (left + right) / 2;
206             
207             if (offset < offsets[midIndex]) {
208                 right = midIndex;
209             } else if (offset >= offsets[midIndex] + lengths[midIndex]) {
210                 left = midIndex + 1;
211             } else {
212                 return midIndex;
213             }
214         }
215         
216         return midIndex;
217     }
218
219     /* (non-Javadoc)
220      * @see org.eclipse.swt.custom.StyledTextContent#getLineCount()
221      */

222     public int getLineCount() {
223         return regionCount;
224     }
225
226     /* (non-Javadoc)
227      * @see org.eclipse.swt.custom.StyledTextContent#getLineDelimiter()
228      */

229     public String JavaDoc getLineDelimiter() {
230         return System.getProperty("line.separator"); //$NON-NLS-1$
231
}
232
233     /* (non-Javadoc)
234      * @see org.eclipse.swt.custom.StyledTextContent#getOffsetAtLine(int)
235      */

236     public int getOffsetAtLine(int lineIndex) {
237         return offsets[lineIndex];
238     }
239
240     /* (non-Javadoc)
241      * @see org.eclipse.swt.custom.StyledTextContent#getTextRange(int, int)
242      */

243     public String JavaDoc getTextRange(int start, int length) {
244         try {
245             return document.get(start, length);
246         } catch (BadLocationException e) {
247         }
248         return null;
249     }
250
251     /* (non-Javadoc)
252      * @see org.eclipse.swt.custom.StyledTextContent#replaceTextRange(int, int, java.lang.String)
253      */

254     public void replaceTextRange(int start, int replaceLength, String JavaDoc text) {
255         try {
256             document.replace(start, replaceLength, text);
257         } catch (BadLocationException e) {
258         }
259     }
260
261     /* (non-Javadoc)
262      * @see org.eclipse.swt.custom.StyledTextContent#setText(java.lang.String)
263      */

264     public synchronized void setText(String JavaDoc text) {
265         TextChangedEvent changeEvent = new TextChangedEvent(this);
266         for (Iterator JavaDoc iter = textChangeListeners.iterator(); iter.hasNext();) {
267             TextChangeListener element = (TextChangeListener) iter.next();
268             element.textSet(changeEvent);
269         }
270     }
271
272     /* (non-Javadoc)
273      * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
274      */

275     public synchronized void documentAboutToBeChanged(DocumentEvent event) {
276         if (document == null) {
277             return;
278         }
279         
280         TextChangingEvent changeEvent = new TextChangingEvent(this);
281         changeEvent.start = event.fOffset;
282         changeEvent.newText = (event.fText == null ? "" : event.fText); //$NON-NLS-1$
283
changeEvent.replaceCharCount = event.fLength;
284         changeEvent.newCharCount = (event.fText == null ? 0 : event.fText.length());
285         
286         int first = getLineAtOffset(event.fOffset);
287         int lOffset = Math.max(event.fOffset + event.fLength - 1, 0);
288         int last = getLineAtOffset(lOffset);
289         changeEvent.replaceLineCount = Math.max(last - first, 0);
290      
291         int newLineCount = countNewLines(event.fText);
292         changeEvent.newLineCount = newLineCount >= 0 ? newLineCount : 0;
293
294         if (changeEvent.newLineCount > offsets.length-regionCount) {
295             growRegionArray(changeEvent.newLineCount);
296         }
297         
298         for (Iterator JavaDoc iter = textChangeListeners.iterator(); iter.hasNext();) {
299             TextChangeListener element = (TextChangeListener) iter.next();
300             element.textChanging(changeEvent);
301         }
302     }
303
304     private void growRegionArray(int minSize) {
305         int size = Math.max(offsets.length*2, minSize*2);
306         int[] newOffsets = new int[size];
307         System.arraycopy(offsets, 0, newOffsets, 0, regionCount);
308         offsets = newOffsets;
309         int[] newLengths = new int[size];
310         System.arraycopy(lengths, 0, newLengths, 0, regionCount);
311         lengths = newLengths;
312     }
313
314     private int countNewLines(String JavaDoc string) {
315         int count = 0;
316         
317         if (string.length() == 0) return 0;
318
319         // work around to
320
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4994840
321
// see bug 84641
322
int offset = string.length() - 1;
323         while (string.charAt(offset) == '\r') {
324             offset--;
325             count++;
326         }
327         if (offset < (string.length() - 1)) {
328             string = string.substring(0, offset);
329         }
330         
331         int lastIndex = 0;
332         int index = 0;
333         
334         Matcher JavaDoc matcher = pattern.matcher(string);
335         
336         while (matcher.find()) {
337             index = matcher.start();
338             
339             if (index == 0)
340                 count++;
341             else if (index!=string.length())
342                 count++;
343             
344             if (consoleWidth > 0) {
345                 int lineLen = index - lastIndex + 1;
346                 if (index == 0) lineLen += lengths[regionCount-1];
347                 count += lineLen/consoleWidth;
348             }
349             
350             lastIndex = index;
351         }
352         return count;
353     }
354
355
356     /* (non-Javadoc)
357      * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
358      */

359     public synchronized void documentChanged(DocumentEvent event) {
360         if (document == null) {
361             return;
362         }
363         
364         repairLines(event.fOffset);
365         
366         TextChangedEvent changeEvent = new TextChangedEvent(this);
367
368         for (Iterator JavaDoc iter = textChangeListeners.iterator(); iter.hasNext();) {
369             TextChangeListener element = (TextChangeListener) iter.next();
370             element.textChanged(changeEvent);
371         }
372     }
373
374     /**
375      * sets consoleWidth, repairs line information, then fires event to the viewer text widget.
376      * @param width The console's width
377      */

378     public void setWidth(int width) {
379         if (width != consoleWidth) {
380             consoleWidth = width;
381             repairLines(0);
382             TextChangedEvent changeEvent = new TextChangedEvent(this);
383             for (Iterator JavaDoc iter = textChangeListeners.iterator(); iter.hasNext();) {
384                 TextChangeListener element = (TextChangeListener) iter.next();
385                 element.textSet(changeEvent);
386             }
387         }
388     }
389 }
390
Popular Tags