1 11 package org.eclipse.core.internal.filebuffers; 12 13 import java.io.BufferedReader ; 14 import java.io.ByteArrayInputStream ; 15 import java.io.IOException ; 16 import java.io.InputStream ; 17 import java.io.InputStreamReader ; 18 import java.io.OutputStream ; 19 import java.io.Reader ; 20 import java.io.SequenceInputStream ; 21 import java.nio.ByteBuffer ; 22 import java.nio.CharBuffer ; 23 import java.nio.charset.CharacterCodingException ; 24 import java.nio.charset.Charset ; 25 import java.nio.charset.CharsetEncoder ; 26 import java.nio.charset.CodingErrorAction ; 27 import java.nio.charset.IllegalCharsetNameException ; 28 import java.nio.charset.UnmappableCharacterException ; 29 import java.nio.charset.UnsupportedCharsetException ; 30 31 import org.eclipse.core.filesystem.EFS; 32 import org.eclipse.core.filesystem.IFileInfo; 33 import org.eclipse.core.filesystem.IFileStore; 34 35 import org.eclipse.core.runtime.CoreException; 36 import org.eclipse.core.runtime.IProgressMonitor; 37 import org.eclipse.core.runtime.IStatus; 38 import org.eclipse.core.runtime.Platform; 39 import org.eclipse.core.runtime.QualifiedName; 40 import org.eclipse.core.runtime.Status; 41 import org.eclipse.core.runtime.content.IContentDescription; 42 import org.eclipse.core.runtime.content.IContentType; 43 44 import org.eclipse.core.resources.IResourceStatus; 45 46 import org.eclipse.core.filebuffers.FileBuffers; 47 import org.eclipse.core.filebuffers.IFileBufferStatusCodes; 48 import org.eclipse.core.filebuffers.IPersistableAnnotationModel; 49 import org.eclipse.core.filebuffers.ITextFileBuffer; 50 51 import org.eclipse.jface.text.Assert; 52 import org.eclipse.jface.text.DocumentEvent; 53 import org.eclipse.jface.text.IDocument; 54 import org.eclipse.jface.text.IDocumentListener; 55 import org.eclipse.jface.text.source.IAnnotationModel; 56 57 60 public class JavaTextFileBuffer extends JavaFileBuffer implements ITextFileBuffer { 61 62 63 private class DocumentListener implements IDocumentListener { 64 65 68 public void documentAboutToBeChanged(DocumentEvent event) { 69 } 70 71 74 public void documentChanged(DocumentEvent event) { 75 fCanBeSaved= true; 76 removeFileBufferContentListeners(); 77 fManager.fireDirtyStateChanged(JavaTextFileBuffer.this, fCanBeSaved); 78 } 79 } 80 81 84 private static final int READER_CHUNK_SIZE= 2048; 85 88 private static final int BUFFER_SIZE= 8 * READER_CHUNK_SIZE; 89 92 private static final IStatus STATUS_OK= new Status(IStatus.OK, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.FileBuffer_status_ok, null); 93 96 private static final IStatus STATUS_ERROR= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.FileBuffer_status_error, null); 97 100 private static final String CHARSET_UTF_8= "UTF-8"; 102 106 private static final QualifiedName[] NO_PROPERTIES= new QualifiedName[0]; 107 108 109 110 protected IDocument fDocument; 111 112 protected String fEncoding; 113 114 protected IDocumentListener fDocumentListener= new DocumentListener(); 115 116 private String fExplicitEncoding; 117 118 private boolean fHasBOM; 119 120 private IAnnotationModel fAnnotationModel; 121 125 private final Object fAnnotationModelCreationLock= new Object (); 126 130 private boolean fIsCacheUpdated= false; 131 132 133 public JavaTextFileBuffer(TextFileBufferManager manager) { 134 super(manager); 135 } 136 137 140 public IDocument getDocument() { 141 return fDocument; 142 } 143 144 147 public IAnnotationModel getAnnotationModel() { 148 synchronized (fAnnotationModelCreationLock) { 149 if (fAnnotationModel == null && !isDisconnected()) { 150 fAnnotationModel= fManager.createAnnotationModel(getLocation()); 151 if (fAnnotationModel != null) 152 fAnnotationModel.connect(fDocument); 153 } 154 } 155 return fAnnotationModel; 156 } 157 158 161 public String getEncoding() { 162 if (!fIsCacheUpdated) 163 cacheEncodingState(null); 164 return fEncoding; 165 } 166 167 170 public void setEncoding(String encoding) { 171 fExplicitEncoding= encoding; 172 if (encoding == null || encoding.equals(fEncoding)) 173 fIsCacheUpdated= false; 174 else { 175 fEncoding= encoding; 176 fHasBOM= false; 177 } 178 } 179 180 183 public IStatus getStatus() { 184 if (!isDisconnected()) { 185 if (fStatus != null) 186 return fStatus; 187 return (fDocument == null ? STATUS_ERROR : STATUS_OK); 188 } 189 return STATUS_ERROR; 190 } 191 192 private InputStream getFileContents(IFileStore fileStore, IProgressMonitor monitor) throws CoreException { 193 if (fileStore == null) 194 return null; 195 196 return fileStore.openInputStream(EFS.NONE, null); 197 } 198 199 private void setFileContents(InputStream stream, boolean overwrite, IProgressMonitor monitor) throws CoreException { 200 OutputStream out= fFileStore.openOutputStream(EFS.NONE, null); 201 try { 202 byte[] buffer= new byte[8192]; 203 while (true) { 204 int bytesRead= -1; 205 try { 206 bytesRead= stream.read(buffer); 207 } catch (IOException e) { 208 } 209 if (bytesRead == -1) 210 break; 211 try { 212 out.write(buffer, 0, bytesRead); 213 } catch (IOException e) { 214 } 215 if (monitor != null) 216 monitor.worked(1); 217 } 218 } finally { 219 try { 220 stream.close(); 221 } catch (IOException e) { 222 } finally { 223 try { 224 out.close(); 225 } catch (IOException e) { 226 } 227 } 228 } 229 } 230 231 234 public void revert(IProgressMonitor monitor) throws CoreException { 235 if (isDisconnected()) 236 return; 237 238 IDocument original= null; 239 fStatus= null; 240 241 try { 242 original= fManager.createEmptyDocument(getLocation()); 243 cacheEncodingState(monitor); 244 setDocumentContent(original, fFileStore, fEncoding, fHasBOM, monitor); 245 } catch (CoreException x) { 246 fStatus= x.getStatus(); 247 } 248 249 if (original == null) 250 return; 251 252 String originalContents= original.get(); 253 boolean replaceContents= !originalContents.equals(fDocument.get()); 254 255 if (!replaceContents && !fCanBeSaved) 256 return; 257 258 fManager.fireStateChanging(this); 259 try { 260 261 if (replaceContents) { 262 fManager.fireBufferContentAboutToBeReplaced(this); 263 fDocument.set(original.get()); 264 } 265 266 boolean fireDirtyStateChanged= fCanBeSaved; 267 if (fCanBeSaved) { 268 fCanBeSaved= false; 269 addFileBufferContentListeners(); 270 } 271 272 if (replaceContents) 273 fManager.fireBufferContentReplaced(this); 274 275 if (fFileStore != null) 276 fSynchronizationStamp= fFileStore.fetchInfo().getLastModified(); 277 278 if (fAnnotationModel instanceof IPersistableAnnotationModel) { 279 IPersistableAnnotationModel persistableModel= (IPersistableAnnotationModel) fAnnotationModel; 280 try { 281 persistableModel.revert(fDocument); 282 } catch (CoreException x) { 283 fStatus= x.getStatus(); 284 } 285 } 286 287 if (fireDirtyStateChanged) 288 fManager.fireDirtyStateChanged(this, fCanBeSaved); 289 290 } catch (RuntimeException x) { 291 fManager.fireStateChangeFailed(this); 292 throw x; 293 } 294 } 295 296 300 public IContentType getContentType () throws CoreException { 301 if (fFileStore == null) 302 return null; 303 304 InputStream stream= null; 305 try { 306 if (isDirty()) { 307 Reader reader= new DocumentReader(getDocument()); 308 try { 309 IContentDescription desc= Platform.getContentTypeManager().getDescriptionFor(reader, fFileStore.getName(), NO_PROPERTIES); 310 if (desc != null && desc.getContentType() != null) 311 return desc.getContentType(); 312 } finally { 313 try { 314 if (reader != null) 315 reader.close(); 316 } catch (IOException ex) { 317 } 318 } 319 } 320 stream= fFileStore.openInputStream(EFS.NONE, null); 321 IContentDescription desc= Platform.getContentTypeManager().getDescriptionFor(stream, fFileStore.getName(), NO_PROPERTIES); 322 if (desc != null && desc.getContentType() != null) 323 return desc.getContentType(); 324 return null; 325 } catch (IOException x) { 326 throw new CoreException(new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, NLSUtility.format(FileBuffersMessages.FileBuffer_error_queryContentDescription, fFileStore.toString()), x)); 327 } finally { 328 try { 329 if (stream != null) 330 stream.close(); 331 } catch (IOException x) { 332 } 333 } 334 } 335 336 339 protected void addFileBufferContentListeners() { 340 if (fDocument != null) 341 fDocument.addDocumentListener(fDocumentListener); 342 } 343 344 347 protected void removeFileBufferContentListeners() { 348 if (fDocument != null) 349 fDocument.removeDocumentListener(fDocumentListener); 350 } 351 352 355 protected void initializeFileBufferContent(IProgressMonitor monitor) throws CoreException { 356 try { 357 fDocument= fManager.createEmptyDocument(getLocation()); 358 cacheEncodingState(monitor); 359 setDocumentContent(fDocument, fFileStore, fEncoding, fHasBOM, monitor); 360 } catch (CoreException x) { 361 fDocument= fManager.createEmptyDocument(getLocation()); 362 fStatus= x.getStatus(); 363 } 364 } 365 366 369 protected void connected() { 370 super.connected(); 371 if (fAnnotationModel != null) 372 fAnnotationModel.connect(fDocument); 373 } 374 375 378 protected void disconnected() { 379 if (fAnnotationModel != null) 380 fAnnotationModel.disconnect(fDocument); 381 super.disconnected(); 382 } 383 384 protected void cacheEncodingState(IProgressMonitor monitor) { 385 fEncoding= fExplicitEncoding; 386 fHasBOM= false; 387 fIsCacheUpdated= true; 388 389 InputStream stream= null; 390 try { 391 stream= getFileContents(fFileStore, monitor); 392 if (stream == null) 393 return; 394 395 QualifiedName[] options= new QualifiedName[] { IContentDescription.CHARSET, IContentDescription.BYTE_ORDER_MARK }; 396 IContentDescription description= Platform.getContentTypeManager().getDescriptionFor(stream, fFileStore.getName(), options); 397 if (description != null) { 398 fHasBOM= description.getProperty(IContentDescription.BYTE_ORDER_MARK) != null; 399 if (fEncoding == null) 400 fEncoding= description.getCharset(); 401 } 402 } catch (CoreException e) { 403 } catch (IOException e) { 405 } finally { 407 try { 408 if (stream != null) 409 stream.close(); 410 } catch (IOException ex) { 411 FileBuffersPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.JavaTextFileBuffer_error_closeStream, ex)); 412 } 413 } 414 } 415 416 419 protected void commitFileBufferContent(IProgressMonitor monitor, boolean overwrite) throws CoreException { 420 String encoding= computeEncoding(); 421 422 Charset charset; 423 try { 424 charset= Charset.forName(encoding); 425 } catch (UnsupportedCharsetException ex) { 426 String message= NLSUtility.format(FileBuffersMessages.ResourceTextFileBuffer_error_unsupported_encoding_message_arg, encoding); 427 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, message, ex); 428 throw new CoreException(s); 429 } catch (IllegalCharsetNameException ex) { 430 String message= NLSUtility.format(FileBuffersMessages.ResourceTextFileBuffer_error_illegal_encoding_message_arg, encoding); 431 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, message, ex); 432 throw new CoreException(s); 433 } 434 435 CharsetEncoder encoder= charset.newEncoder(); 436 encoder.onMalformedInput(CodingErrorAction.REPLACE); 437 encoder.onUnmappableCharacter(CodingErrorAction.REPORT); 438 439 byte[] bytes; 440 int bytesLength; 441 442 try { 443 ByteBuffer byteBuffer= encoder.encode(CharBuffer.wrap(fDocument.get())); 444 bytesLength= byteBuffer.limit(); 445 if (byteBuffer.hasArray()) 446 bytes= byteBuffer.array(); 447 else { 448 bytes= new byte[bytesLength]; 449 byteBuffer.get(bytes); 450 } 451 } catch (CharacterCodingException ex) { 452 Assert.isTrue(ex instanceof UnmappableCharacterException ); 453 String message= NLSUtility.format(FileBuffersMessages.ResourceTextFileBuffer_error_charset_mapping_failed_message_arg, encoding); 454 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IFileBufferStatusCodes.CHARSET_MAPPING_FAILED, message, null); 455 throw new CoreException(s); 456 } 457 458 IFileInfo fileInfo= fFileStore == null ? null : fFileStore.fetchInfo(); 459 if (fileInfo != null && fileInfo.exists()) { 460 461 if (!overwrite) 462 checkSynchronizationState(); 463 464 InputStream stream= new ByteArrayInputStream (bytes, 0, bytesLength); 465 466 471 if (fHasBOM && CHARSET_UTF_8.equals(encoding)) 472 stream= new SequenceInputStream (new ByteArrayInputStream (IContentDescription.BOM_UTF_8), stream); 473 474 475 setFileContents(stream, overwrite, monitor); 478 fSynchronizationStamp= fFileStore.fetchInfo().getLastModified(); 480 481 if (fAnnotationModel instanceof IPersistableAnnotationModel) { 482 IPersistableAnnotationModel persistableModel= (IPersistableAnnotationModel) fAnnotationModel; 483 persistableModel.commit(fDocument); 484 } 485 486 } else { 487 488 fFileStore= FileBuffers.getFileStoreAtLocation(getLocation()); 489 fFileStore.getParent().mkdir(EFS.NONE, null); 490 OutputStream out= fFileStore.openOutputStream(EFS.NONE, null); 491 try { 492 497 if (fHasBOM && CHARSET_UTF_8.equals(encoding)) 498 out.write(IContentDescription.BOM_UTF_8); 499 500 out.write(bytes); 501 out.flush(); 502 } catch (IOException x) { 503 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, x.getLocalizedMessage(), x); 504 throw new CoreException(s); 505 } finally { 506 try { 507 out.close(); 508 } catch (IOException x) { 509 } 510 } 511 512 fSynchronizationStamp= fFileStore.fetchInfo().getLastModified(); 514 515 } 516 } 517 518 private String computeEncoding() { 519 if (!fIsCacheUpdated) 521 cacheEncodingState(null); 522 523 if (fExplicitEncoding != null) 525 return fExplicitEncoding; 526 527 if (fFileStore != null) { 528 Reader reader= new DocumentReader(fDocument); 530 try { 531 QualifiedName[] options= new QualifiedName[] { IContentDescription.CHARSET, IContentDescription.BYTE_ORDER_MARK }; 532 IContentDescription description= Platform.getContentTypeManager().getDescriptionFor(reader, fFileStore.getName(), options); 533 if (description != null) { 534 String encoding= description.getCharset(); 535 if (encoding != null) 536 return encoding; 537 } 538 } catch (IOException ex) { 539 } finally { 541 try { 542 if (reader != null) 543 reader.close(); 544 } catch (IOException x) { 545 } 546 } 547 } 548 549 if (fHasBOM) 551 return fEncoding; 552 553 return fManager.getDefaultEncoding(); 555 } 556 557 567 private void setDocumentContent(IDocument document, IFileStore file, String encoding, boolean hasBOM, IProgressMonitor monitor) throws CoreException { 568 InputStream contentStream= getFileContents(file, monitor); 569 if (contentStream == null) 570 return; 571 572 Reader in= null; 573 try { 574 575 if (encoding == null) 576 encoding= fManager.getDefaultEncoding(); 577 578 583 if (hasBOM && CHARSET_UTF_8.equals(encoding)) { 584 int n= 0; 585 do { 586 int bytes= contentStream.read(new byte[IContentDescription.BOM_UTF_8.length]); 587 if (bytes == -1) 588 throw new IOException (); 589 n += bytes; 590 } while (n < IContentDescription.BOM_UTF_8.length); 591 } 592 593 in= new BufferedReader (new InputStreamReader (contentStream, encoding), BUFFER_SIZE); 594 StringBuffer buffer= new StringBuffer (BUFFER_SIZE); 595 char[] readBuffer= new char[READER_CHUNK_SIZE]; 596 int n= in.read(readBuffer); 597 while (n > 0) { 598 buffer.append(readBuffer, 0, n); 599 n= in.read(readBuffer); 600 } 601 602 document.set(buffer.toString()); 603 604 } catch (IOException x) { 605 String msg= x.getMessage() == null ? "" : x.getMessage(); IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, msg, x); 607 throw new CoreException(s); 608 } finally { 609 try { 610 if (in != null) 611 in.close(); 612 else 613 contentStream.close(); 614 } catch (IOException x) { 615 } 616 } 617 } 618 619 625 private void checkSynchronizationState() throws CoreException { 626 if (!isSynchronized()) { 627 Status status= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IResourceStatus.OUT_OF_SYNC_LOCAL, FileBuffersMessages.FileBuffer_error_outOfSync, null); 628 throw new CoreException(status); 629 } 630 } 631 } 632 | Popular Tags |