KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > Buffer


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  * Tim Hanson (thanson@bea.com) - patch for https://bugs.eclipse.org/bugs/show_bug.cgi?id=126673
11  *******************************************************************************/

12 package org.eclipse.jdt.internal.core;
13
14 import java.io.ByteArrayInputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.util.ArrayList JavaDoc;
17
18 import org.eclipse.core.resources.IFile;
19 import org.eclipse.core.resources.IResource;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.ISafeRunnable;
23 import org.eclipse.core.runtime.SafeRunner;
24 import org.eclipse.core.runtime.content.IContentDescription;
25 import org.eclipse.jdt.core.*;
26 import org.eclipse.jdt.internal.core.util.Util;
27
28 /**
29  * @see IBuffer
30  */

31 public class Buffer implements IBuffer {
32     protected IFile file;
33     protected int flags;
34     protected char[] contents;
35     protected ArrayList JavaDoc changeListeners;
36     protected IOpenable owner;
37     protected int gapStart = -1;
38     protected int gapEnd = -1;
39
40     protected Object JavaDoc lock = new Object JavaDoc();
41
42     protected static final int F_HAS_UNSAVED_CHANGES = 1;
43     protected static final int F_IS_READ_ONLY = 2;
44     protected static final int F_IS_CLOSED = 4;
45
46 /**
47  * Creates a new buffer on an underlying resource.
48  */

49 protected Buffer(IFile file, IOpenable owner, boolean readOnly) {
50     this.file = file;
51     this.owner = owner;
52     if (file == null) {
53         setReadOnly(readOnly);
54     }
55 }
56 /**
57  * @see IBuffer
58  */

59 public synchronized void addBufferChangedListener(IBufferChangedListener listener) {
60     if (this.changeListeners == null) {
61         this.changeListeners = new ArrayList JavaDoc(5);
62     }
63     if (!this.changeListeners.contains(listener)) {
64         this.changeListeners.add(listener);
65     }
66 }
67 /**
68  * Append the <code>text</code> to the actual content, the gap is moved
69  * to the end of the <code>text</code>.
70  */

71 public void append(char[] text) {
72     if (!isReadOnly()) {
73         if (text == null || text.length == 0) {
74             return;
75         }
76         int length = getLength();
77         synchronized(this.lock) {
78             if (this.contents == null) return;
79             moveAndResizeGap(length, text.length);
80             System.arraycopy(text, 0, this.contents, length, text.length);
81             this.gapStart += text.length;
82             this.flags |= F_HAS_UNSAVED_CHANGES;
83         }
84         notifyChanged(new BufferChangedEvent(this, length, 0, new String JavaDoc(text)));
85     }
86 }
87 /**
88  * Append the <code>text</code> to the actual content, the gap is moved
89  * to the end of the <code>text</code>.
90  */

91 public void append(String JavaDoc text) {
92     if (text == null) {
93         return;
94     }
95     this.append(text.toCharArray());
96 }
97 /**
98  * @see IBuffer
99  */

100 public void close() {
101     BufferChangedEvent event = null;
102     synchronized (this.lock) {
103         if (isClosed())
104             return;
105         event = new BufferChangedEvent(this, 0, 0, null);
106         this.contents = null;
107         this.flags |= F_IS_CLOSED;
108     }
109     notifyChanged(event); // notify outside of synchronized block
110
synchronized(this) { // ensure that no other thread is adding/removing a listener at the same time (https://bugs.eclipse.org/bugs/show_bug.cgi?id=126673)
111
this.changeListeners = null;
112     }
113 }
114 /**
115  * @see IBuffer
116  */

117 public char getChar(int position) {
118     synchronized (this.lock) {
119         if (this.contents == null) return Character.MIN_VALUE;
120         if (position < this.gapStart) {
121             return this.contents[position];
122         }
123         int gapLength = this.gapEnd - this.gapStart;
124         return this.contents[position + gapLength];
125     }
126 }
127 /**
128  * @see IBuffer
129  */

130 public char[] getCharacters() {
131     synchronized (this.lock) {
132         if (this.contents == null) return null;
133         if (this.gapStart < 0) {
134             return this.contents;
135         }
136         int length = this.contents.length;
137         char[] newContents = new char[length - this.gapEnd + this.gapStart];
138         System.arraycopy(this.contents, 0, newContents, 0, this.gapStart);
139         System.arraycopy(this.contents, this.gapEnd, newContents, this.gapStart, length - this.gapEnd);
140         return newContents;
141     }
142 }
143 /**
144  * @see IBuffer
145  */

146 public String JavaDoc getContents() {
147     char[] chars = this.getCharacters();
148     if (chars == null) return null;
149     return new String JavaDoc(chars);
150 }
151 /**
152  * @see IBuffer
153  */

154 public int getLength() {
155     synchronized (this.lock) {
156         if (this.contents == null) return -1;
157         int length = this.gapEnd - this.gapStart;
158         return (this.contents.length - length);
159     }
160 }
161 /**
162  * @see IBuffer
163  */

164 public IOpenable getOwner() {
165     return this.owner;
166 }
167 /**
168  * @see IBuffer
169  */

170 public String JavaDoc getText(int offset, int length) {
171     synchronized (this.lock) {
172         if (this.contents == null) return ""; //$NON-NLS-1$
173
if (offset + length < this.gapStart)
174             return new String JavaDoc(this.contents, offset, length);
175         if (this.gapStart < offset) {
176             int gapLength = this.gapEnd - this.gapStart;
177             return new String JavaDoc(this.contents, offset + gapLength, length);
178         }
179         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
180         buf.append(this.contents, offset, this.gapStart - offset);
181         buf.append(this.contents, this.gapEnd, offset + length - this.gapStart);
182         return buf.toString();
183     }
184 }
185 /**
186  * @see IBuffer
187  */

188 public IResource getUnderlyingResource() {
189     return this.file;
190 }
191 /**
192  * @see IBuffer
193  */

194 public boolean hasUnsavedChanges() {
195     return (this.flags & F_HAS_UNSAVED_CHANGES) != 0;
196 }
197 /**
198  * @see IBuffer
199  */

200 public boolean isClosed() {
201     return (this.flags & F_IS_CLOSED) != 0;
202 }
203 /**
204  * @see IBuffer
205  */

206 public boolean isReadOnly() {
207     return (this.flags & F_IS_READ_ONLY) != 0;
208 }
209 /**
210  * Moves the gap to location and adjust its size to the
211  * anticipated change size. The size represents the expected
212  * range of the gap that will be filled after the gap has been moved.
213  * Thus the gap is resized to actual size + the specified size and
214  * moved to the given position.
215  */

216 protected void moveAndResizeGap(int position, int size) {
217     char[] content = null;
218     int oldSize = this.gapEnd - this.gapStart;
219     if (size < 0) {
220         if (oldSize > 0) {
221             content = new char[this.contents.length - oldSize];
222             System.arraycopy(this.contents, 0, content, 0, this.gapStart);
223             System.arraycopy(this.contents, this.gapEnd, content, this.gapStart, content.length - this.gapStart);
224             this.contents = content;
225         }
226         this.gapStart = this.gapEnd = position;
227         return;
228     }
229     content = new char[this.contents.length + (size - oldSize)];
230     int newGapStart = position;
231     int newGapEnd = newGapStart + size;
232     if (oldSize == 0) {
233         System.arraycopy(this.contents, 0, content, 0, newGapStart);
234         System.arraycopy(this.contents, newGapStart, content, newGapEnd, content.length - newGapEnd);
235     } else
236         if (newGapStart < this.gapStart) {
237             int delta = this.gapStart - newGapStart;
238             System.arraycopy(this.contents, 0, content, 0, newGapStart);
239             System.arraycopy(this.contents, newGapStart, content, newGapEnd, delta);
240             System.arraycopy(this.contents, this.gapEnd, content, newGapEnd + delta, this.contents.length - this.gapEnd);
241         } else {
242             int delta = newGapStart - this.gapStart;
243             System.arraycopy(this.contents, 0, content, 0, this.gapStart);
244             System.arraycopy(this.contents, this.gapEnd, content, this.gapStart, delta);
245             System.arraycopy(this.contents, this.gapEnd + delta, content, newGapEnd, content.length - newGapEnd);
246         }
247     this.contents = content;
248     this.gapStart = newGapStart;
249     this.gapEnd = newGapEnd;
250 }
251 /**
252  * Notify the listeners that this buffer has changed.
253  * To avoid deadlock, this should not be called in a synchronized block.
254  */

255 protected void notifyChanged(final BufferChangedEvent event) {
256     ArrayList JavaDoc listeners = this.changeListeners;
257     if (listeners != null) {
258         for (int i = 0, size = listeners.size(); i < size; ++i) {
259             final IBufferChangedListener listener = (IBufferChangedListener) listeners.get(i);
260             SafeRunner.run(new ISafeRunnable() {
261                 public void handleException(Throwable JavaDoc exception) {
262                     Util.log(exception, "Exception occurred in listener of buffer change notification"); //$NON-NLS-1$
263
}
264                 public void run() throws Exception JavaDoc {
265                     listener.bufferChanged(event);
266                 }
267             });
268             
269         }
270     }
271 }
272 /**
273  * @see IBuffer
274  */

275 public synchronized void removeBufferChangedListener(IBufferChangedListener listener) {
276     if (this.changeListeners != null) {
277         this.changeListeners.remove(listener);
278         if (this.changeListeners.size() == 0) {
279             this.changeListeners = null;
280         }
281     }
282 }
283 /**
284  * Replaces <code>length</code> characters starting from <code>position</code> with <code>text<code>.
285  * After that operation, the gap is placed at the end of the
286  * inserted <code>text</code>.
287  */

288 public void replace(int position, int length, char[] text) {
289     if (!isReadOnly()) {
290         int textLength = text == null ? 0 : text.length;
291         synchronized (this.lock) {
292             if (this.contents == null) return;
293             
294             // move gap
295
moveAndResizeGap(position + length, textLength - length);
296
297             // overwrite
298
int min = Math.min(textLength, length);
299             if (min > 0) {
300                 System.arraycopy(text, 0, this.contents, position, min);
301             }
302             if (length > textLength) {
303                 // enlarge the gap
304
this.gapStart -= length - textLength;
305             } else if (textLength > length) {
306                 // shrink gap
307
this.gapStart += textLength - length;
308                 System.arraycopy(text, 0, this.contents, position, textLength);
309             }
310             this.flags |= F_HAS_UNSAVED_CHANGES;
311         }
312         String JavaDoc string = null;
313         if (textLength > 0) {
314             string = new String JavaDoc(text);
315         }
316         notifyChanged(new BufferChangedEvent(this, position, length, string));
317     }
318 }
319 /**
320  * Replaces <code>length</code> characters starting from <code>position</code> with <code>text<code>.
321  * After that operation, the gap is placed at the end of the
322  * inserted <code>text</code>.
323  */

324 public void replace(int position, int length, String JavaDoc text) {
325     this.replace(position, length, text == null ? null : text.toCharArray());
326 }
327 /**
328  * @see IBuffer
329  */

330 public void save(IProgressMonitor progress, boolean force) throws JavaModelException {
331
332     // determine if saving is required
333
if (isReadOnly() || this.file == null) {
334         return;
335     }
336     if (!hasUnsavedChanges())
337         return;
338         
339     // use a platform operation to update the resource contents
340
try {
341         String JavaDoc stringContents = this.getContents();
342         if (stringContents == null) return;
343
344         // Get encoding
345
String JavaDoc encoding = null;
346         try {
347             encoding = this.file.getCharset();
348         }
349         catch (CoreException ce) {
350             // use no encoding
351
}
352         
353         // Create bytes array
354
byte[] bytes = encoding == null
355             ? stringContents.getBytes()
356             : stringContents.getBytes(encoding);
357
358         // Special case for UTF-8 BOM files
359
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=110576
360
if (encoding != null && encoding.equals(org.eclipse.jdt.internal.compiler.util.Util.UTF_8)) {
361             IContentDescription description = this.file.getContentDescription();
362             if (description != null && description.getProperty(IContentDescription.BYTE_ORDER_MARK) != null) {
363                 int bomLength= IContentDescription.BOM_UTF_8.length;
364                 byte[] bytesWithBOM= new byte[bytes.length + bomLength];
365                 System.arraycopy(IContentDescription.BOM_UTF_8, 0, bytesWithBOM, 0, bomLength);
366                 System.arraycopy(bytes, 0, bytesWithBOM, bomLength, bytes.length);
367                 bytes= bytesWithBOM;
368             }
369         }
370         
371         // Set file contents
372
ByteArrayInputStream JavaDoc stream = new ByteArrayInputStream JavaDoc(bytes);
373         if (this.file.exists()) {
374             this.file.setContents(
375                 stream,
376                 force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
377                 null);
378         } else {
379             this.file.create(stream, force, null);
380         }
381     } catch (IOException JavaDoc e) {
382         throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
383     } catch (CoreException e) {
384         throw new JavaModelException(e);
385     }
386
387     // the resource no longer has unsaved changes
388
this.flags &= ~ (F_HAS_UNSAVED_CHANGES);
389 }
390 /**
391  * @see IBuffer
392  */

393 public void setContents(char[] newContents) {
394     // allow special case for first initialization
395
// after creation by buffer factory
396
if (this.contents == null) {
397         synchronized (this.lock) {
398             this.contents = newContents;
399             this.flags &= ~ (F_HAS_UNSAVED_CHANGES);
400         }
401         return;
402     }
403     
404     if (!isReadOnly()) {
405         String JavaDoc string = null;
406         if (newContents != null) {
407             string = new String JavaDoc(newContents);
408         }
409         synchronized (this.lock) {
410             if (this.contents == null) return; // ignore if buffer is closed (as per spec)
411
this.contents = newContents;
412             this.flags |= F_HAS_UNSAVED_CHANGES;
413             this.gapStart = -1;
414             this.gapEnd = -1;
415         }
416         BufferChangedEvent event = new BufferChangedEvent(this, 0, this.getLength(), string);
417         notifyChanged(event);
418     }
419 }
420 /**
421  * @see IBuffer
422  */

423 public void setContents(String JavaDoc newContents) {
424     this.setContents(newContents.toCharArray());
425 }
426 /**
427  * Sets this <code>Buffer</code> to be read only.
428  */

429 protected void setReadOnly(boolean readOnly) {
430     if (readOnly) {
431         this.flags |= F_IS_READ_ONLY;
432     } else {
433         this.flags &= ~(F_IS_READ_ONLY);
434     }
435 }
436 public String JavaDoc toString() {
437     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
438     buffer.append("Owner: " + ((JavaElement)this.owner).toStringWithAncestors()); //$NON-NLS-1$
439
buffer.append("\nHas unsaved changes: " + this.hasUnsavedChanges()); //$NON-NLS-1$
440
buffer.append("\nIs readonly: " + this.isReadOnly()); //$NON-NLS-1$
441
buffer.append("\nIs closed: " + this.isClosed()); //$NON-NLS-1$
442
buffer.append("\nContents:\n"); //$NON-NLS-1$
443
char[] charContents = this.getCharacters();
444     if (charContents == null) {
445         buffer.append("<null>"); //$NON-NLS-1$
446
} else {
447         int length = charContents.length;
448         for (int i = 0; i < length; i++) {
449             char c = charContents[i];
450             switch (c) {
451                 case '\n':
452                     buffer.append("\\n\n"); //$NON-NLS-1$
453
break;
454                 case '\r':
455                     if (i < length-1 && this.contents[i+1] == '\n') {
456                         buffer.append("\\r\\n\n"); //$NON-NLS-1$
457
i++;
458                     } else {
459                         buffer.append("\\r\n"); //$NON-NLS-1$
460
}
461                     break;
462                 default:
463                     buffer.append(c);
464                     break;
465             }
466         }
467     }
468     return buffer.toString();
469 }
470 }
471
Popular Tags