1 12 package org.eclipse.jdt.internal.core; 13 14 import java.io.ByteArrayInputStream ; 15 import java.io.IOException ; 16 import java.util.ArrayList ; 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 31 public class Buffer implements IBuffer { 32 protected IFile file; 33 protected int flags; 34 protected char[] contents; 35 protected ArrayList changeListeners; 36 protected IOpenable owner; 37 protected int gapStart = -1; 38 protected int gapEnd = -1; 39 40 protected Object lock = new Object (); 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 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 59 public synchronized void addBufferChangedListener(IBufferChangedListener listener) { 60 if (this.changeListeners == null) { 61 this.changeListeners = new ArrayList (5); 62 } 63 if (!this.changeListeners.contains(listener)) { 64 this.changeListeners.add(listener); 65 } 66 } 67 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 (text))); 85 } 86 } 87 91 public void append(String text) { 92 if (text == null) { 93 return; 94 } 95 this.append(text.toCharArray()); 96 } 97 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); synchronized(this) { this.changeListeners = null; 112 } 113 } 114 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 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 146 public String getContents() { 147 char[] chars = this.getCharacters(); 148 if (chars == null) return null; 149 return new String (chars); 150 } 151 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 164 public IOpenable getOwner() { 165 return this.owner; 166 } 167 170 public String getText(int offset, int length) { 171 synchronized (this.lock) { 172 if (this.contents == null) return ""; if (offset + length < this.gapStart) 174 return new String (this.contents, offset, length); 175 if (this.gapStart < offset) { 176 int gapLength = this.gapEnd - this.gapStart; 177 return new String (this.contents, offset + gapLength, length); 178 } 179 StringBuffer buf = new StringBuffer (); 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 188 public IResource getUnderlyingResource() { 189 return this.file; 190 } 191 194 public boolean hasUnsavedChanges() { 195 return (this.flags & F_HAS_UNSAVED_CHANGES) != 0; 196 } 197 200 public boolean isClosed() { 201 return (this.flags & F_IS_CLOSED) != 0; 202 } 203 206 public boolean isReadOnly() { 207 return (this.flags & F_IS_READ_ONLY) != 0; 208 } 209 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 255 protected void notifyChanged(final BufferChangedEvent event) { 256 ArrayList 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 exception) { 262 Util.log(exception, "Exception occurred in listener of buffer change notification"); } 264 public void run() throws Exception { 265 listener.bufferChanged(event); 266 } 267 }); 268 269 } 270 } 271 } 272 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 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 moveAndResizeGap(position + length, textLength - length); 296 297 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 this.gapStart -= length - textLength; 305 } else if (textLength > length) { 306 this.gapStart += textLength - length; 308 System.arraycopy(text, 0, this.contents, position, textLength); 309 } 310 this.flags |= F_HAS_UNSAVED_CHANGES; 311 } 312 String string = null; 313 if (textLength > 0) { 314 string = new String (text); 315 } 316 notifyChanged(new BufferChangedEvent(this, position, length, string)); 317 } 318 } 319 324 public void replace(int position, int length, String text) { 325 this.replace(position, length, text == null ? null : text.toCharArray()); 326 } 327 330 public void save(IProgressMonitor progress, boolean force) throws JavaModelException { 331 332 if (isReadOnly() || this.file == null) { 334 return; 335 } 336 if (!hasUnsavedChanges()) 337 return; 338 339 try { 341 String stringContents = this.getContents(); 342 if (stringContents == null) return; 343 344 String encoding = null; 346 try { 347 encoding = this.file.getCharset(); 348 } 349 catch (CoreException ce) { 350 } 352 353 byte[] bytes = encoding == null 355 ? stringContents.getBytes() 356 : stringContents.getBytes(encoding); 357 358 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 ByteArrayInputStream stream = new ByteArrayInputStream (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 e) { 382 throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); 383 } catch (CoreException e) { 384 throw new JavaModelException(e); 385 } 386 387 this.flags &= ~ (F_HAS_UNSAVED_CHANGES); 389 } 390 393 public void setContents(char[] newContents) { 394 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 string = null; 406 if (newContents != null) { 407 string = new String (newContents); 408 } 409 synchronized (this.lock) { 410 if (this.contents == null) return; 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 423 public void setContents(String newContents) { 424 this.setContents(newContents.toCharArray()); 425 } 426 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 toString() { 437 StringBuffer buffer = new StringBuffer (); 438 buffer.append("Owner: " + ((JavaElement)this.owner).toStringWithAncestors()); buffer.append("\nHas unsaved changes: " + this.hasUnsavedChanges()); buffer.append("\nIs readonly: " + this.isReadOnly()); buffer.append("\nIs closed: " + this.isClosed()); buffer.append("\nContents:\n"); char[] charContents = this.getCharacters(); 444 if (charContents == null) { 445 buffer.append("<null>"); } 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"); break; 454 case '\r': 455 if (i < length-1 && this.contents[i+1] == '\n') { 456 buffer.append("\\r\\n\n"); i++; 458 } else { 459 buffer.append("\\r\n"); } 461 break; 462 default: 463 buffer.append(c); 464 break; 465 } 466 } 467 } 468 return buffer.toString(); 469 } 470 } 471 | Popular Tags |