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.Reader ; 19 import java.io.SequenceInputStream ; 20 import java.nio.ByteBuffer ; 21 import java.nio.CharBuffer ; 22 import java.nio.charset.CharacterCodingException ; 23 import java.nio.charset.Charset ; 24 import java.nio.charset.CharsetEncoder ; 25 import java.nio.charset.CodingErrorAction ; 26 import java.nio.charset.IllegalCharsetNameException ; 27 import java.nio.charset.UnmappableCharacterException ; 28 import java.nio.charset.UnsupportedCharsetException ; 29 30 import org.eclipse.core.runtime.Assert; 31 import org.eclipse.core.runtime.CoreException; 32 import org.eclipse.core.runtime.IProgressMonitor; 33 import org.eclipse.core.runtime.IStatus; 34 import org.eclipse.core.runtime.Platform; 35 import org.eclipse.core.runtime.QualifiedName; 36 import org.eclipse.core.runtime.Status; 37 import org.eclipse.core.runtime.SubProgressMonitor; 38 import org.eclipse.core.runtime.content.IContentDescription; 39 import org.eclipse.core.runtime.content.IContentType; 40 41 import org.eclipse.core.resources.IFile; 42 43 import org.eclipse.core.filebuffers.IFileBufferStatusCodes; 44 import org.eclipse.core.filebuffers.IPersistableAnnotationModel; 45 import org.eclipse.core.filebuffers.ITextFileBuffer; 46 import org.eclipse.core.filebuffers.manipulation.ContainerCreator; 47 48 import org.eclipse.jface.text.DocumentEvent; 49 import org.eclipse.jface.text.IDocument; 50 import org.eclipse.jface.text.IDocumentExtension4; 51 import org.eclipse.jface.text.IDocumentListener; 52 import org.eclipse.jface.text.source.IAnnotationModel; 53 54 57 public class ResourceTextFileBuffer extends ResourceFileBuffer implements ITextFileBuffer { 58 59 private class DocumentListener implements IDocumentListener { 60 61 64 public void documentAboutToBeChanged(DocumentEvent event) { 65 } 66 67 70 public void documentChanged(DocumentEvent event) { 71 if (fCanBeSaved && fSynchronizationStamp == event.getModificationStamp()) { 72 fCanBeSaved= false; 73 fManager.fireDirtyStateChanged(ResourceTextFileBuffer.this, fCanBeSaved); 74 } else if (!fCanBeSaved) { 75 fCanBeSaved= true; 76 fManager.fireDirtyStateChanged(ResourceTextFileBuffer.this, fCanBeSaved); 77 } 78 } 79 } 80 81 84 static final private int READER_CHUNK_SIZE= 2048; 85 88 static final private int BUFFER_SIZE= 8 * READER_CHUNK_SIZE; 89 92 static final private QualifiedName ENCODING_KEY= new QualifiedName(FileBuffersPlugin.PLUGIN_ID, "encoding"); 96 static final private IStatus STATUS_OK= new Status(IStatus.OK, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.FileBuffer_status_ok, null); 97 100 static final private IStatus STATUS_ERROR= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.FileBuffer_status_error, null); 101 104 private static final String CHARSET_UTF_8= "UTF-8"; 106 110 private static final QualifiedName[] NO_PROPERTIES= new QualifiedName[0]; 111 112 113 114 protected IDocument fDocument; 115 116 protected String fEncoding; 117 118 protected IDocumentListener fDocumentListener= new DocumentListener(); 119 120 protected IAnnotationModel fAnnotationModel; 121 122 private String fExplicitEncoding; 123 124 private boolean fHasBOM; 125 129 private final Object fAnnotationModelCreationLock= new Object (); 130 131 132 public ResourceTextFileBuffer(ResourceTextFileBufferManager manager) { 133 super(manager); 134 } 135 136 139 public IDocument getDocument() { 140 return fDocument; 141 } 142 143 146 public IAnnotationModel getAnnotationModel() { 147 synchronized (fAnnotationModelCreationLock) { 148 if (fAnnotationModel == null && !isDisconnected()) { 149 fAnnotationModel= fManager.createAnnotationModel(fFile); 150 if (fAnnotationModel != null) 151 fAnnotationModel.connect(fDocument); 152 } 153 } 154 return fAnnotationModel; 155 } 156 157 160 public String getEncoding() { 161 return fEncoding; 162 } 163 164 167 public void setEncoding(String encoding) { 168 fEncoding= encoding; 169 fExplicitEncoding= encoding; 170 fHasBOM= false; 171 try { 172 fFile.setCharset(encoding, null); 173 if (encoding == null) 174 fEncoding= fFile.getCharset(); 175 setHasBOM(); 176 } catch (CoreException x) { 177 handleCoreException(x); 178 } 179 } 180 181 184 public IStatus getStatus() { 185 if (!isDisconnected()) { 186 if (fStatus != null) 187 return fStatus; 188 return (fDocument == null ? STATUS_ERROR : STATUS_OK); 189 } 190 return STATUS_ERROR; 191 } 192 193 197 public IContentType getContentType() throws CoreException { 198 try { 199 if (isDirty()) { 200 Reader reader= null; 201 try { 202 reader= new DocumentReader(getDocument()); 203 IContentDescription desc= Platform.getContentTypeManager().getDescriptionFor(reader, fFile.getName(), NO_PROPERTIES); 204 if (desc != null && desc.getContentType() != null) 205 return desc.getContentType(); 206 } finally { 207 try { 208 if (reader != null) 209 reader.close(); 210 } catch (IOException x) { 211 } 212 } 213 } 214 IContentDescription desc= fFile.getContentDescription(); 215 if (desc != null && desc.getContentType() != null) 216 return desc.getContentType(); 217 return null; 218 } catch (IOException x) { 219 throw new CoreException(new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, NLSUtility.format(FileBuffersMessages.FileBuffer_error_queryContentDescription, fFile.getFullPath().toOSString()), x)); 220 } 221 } 222 223 226 protected void addFileBufferContentListeners() { 227 if (fDocument != null) 228 fDocument.addDocumentListener(fDocumentListener); 229 } 230 231 234 protected void removeFileBufferContentListeners() { 235 if (fDocument != null) 236 fDocument.removeDocumentListener(fDocumentListener); 237 } 238 239 242 protected void initializeFileBufferContent(IProgressMonitor monitor) throws CoreException { 243 try { 244 fEncoding= null; 245 fExplicitEncoding= null; 246 try { 247 fEncoding= fFile.getPersistentProperty(ENCODING_KEY); 248 } catch (CoreException x) { 249 } 251 if (fEncoding != null) { 252 try { 254 fExplicitEncoding= fEncoding; 255 fFile.setCharset(fEncoding, monitor); 256 fFile.setPersistentProperty(ENCODING_KEY, null); 258 } catch (CoreException ex) { 259 handleCoreException(ex); 261 } 262 setHasBOM(); 263 } else { 264 cacheEncodingState(); 265 } 266 267 268 fDocument= fManager.createEmptyDocument(fFile); 269 setDocumentContent(fDocument, fFile, fEncoding); 270 271 } catch (CoreException x) { 272 fDocument= fManager.createEmptyDocument(fFile); 273 fStatus= x.getStatus(); 274 } 275 } 276 277 282 protected void setHasBOM() throws CoreException { 283 fHasBOM= false; 284 IContentDescription description= fFile.getContentDescription(); 285 fHasBOM= description != null && description.getProperty(IContentDescription.BYTE_ORDER_MARK) != null; 286 } 287 288 291 protected void connected() { 292 super.connected(); 293 if (fAnnotationModel != null) 294 fAnnotationModel.connect(fDocument); 295 } 296 297 300 protected void dispose() { 301 if (fAnnotationModel != null) 302 fAnnotationModel.disconnect(fDocument); 303 fDocument= null; 304 super.dispose(); 305 } 306 307 310 protected void commitFileBufferContent(IProgressMonitor monitor, boolean overwrite) throws CoreException { 311 String encoding= computeEncoding(); 312 313 Charset charset; 314 try { 315 charset= Charset.forName(encoding); 316 } catch (UnsupportedCharsetException ex) { 317 String message= NLSUtility.format(FileBuffersMessages.ResourceTextFileBuffer_error_unsupported_encoding_message_arg, encoding); 318 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, message, ex); 319 throw new CoreException(s); 320 } catch (IllegalCharsetNameException ex) { 321 String message= NLSUtility.format(FileBuffersMessages.ResourceTextFileBuffer_error_illegal_encoding_message_arg, encoding); 322 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, message, ex); 323 throw new CoreException(s); 324 } 325 326 CharsetEncoder encoder= charset.newEncoder(); 327 encoder.onMalformedInput(CodingErrorAction.REPLACE); 328 encoder.onUnmappableCharacter(CodingErrorAction.REPORT); 329 330 InputStream stream; 331 332 try { 333 byte[] bytes; 334 ByteBuffer byteBuffer= encoder.encode(CharBuffer.wrap(fDocument.get())); 335 if (byteBuffer.hasArray()) 336 bytes= byteBuffer.array(); 337 else { 338 bytes= new byte[byteBuffer.limit()]; 339 byteBuffer.get(bytes); 340 } 341 stream= new ByteArrayInputStream (bytes, 0, byteBuffer.limit()); 342 } catch (CharacterCodingException ex) { 343 Assert.isTrue(ex instanceof UnmappableCharacterException ); 344 String message= NLSUtility.format(FileBuffersMessages.ResourceTextFileBuffer_error_charset_mapping_failed_message_arg, encoding); 345 IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IFileBufferStatusCodes.CHARSET_MAPPING_FAILED, message, null); 346 throw new CoreException(s); 347 } 348 349 354 if (fHasBOM && CHARSET_UTF_8.equals(encoding)) 355 stream= new SequenceInputStream (new ByteArrayInputStream (IContentDescription.BOM_UTF_8), stream); 356 357 if (fFile.exists()) { 358 359 fFile.setContents(stream, overwrite, true, monitor); 362 364 if (fDocument instanceof IDocumentExtension4) { 365 fSynchronizationStamp= ((IDocumentExtension4)fDocument).getModificationStamp(); 366 fFile.revertModificationStamp(fSynchronizationStamp); 367 } else 368 fSynchronizationStamp= fFile.getModificationStamp(); 369 370 if (fAnnotationModel instanceof IPersistableAnnotationModel) { 371 IPersistableAnnotationModel persistableModel= (IPersistableAnnotationModel) fAnnotationModel; 372 persistableModel.commit(fDocument); 373 } 374 375 } else { 376 377 monitor= Progress.getMonitor(monitor); 378 try { 379 monitor.beginTask(FileBuffersMessages.ResourceTextFileBuffer_task_saving, 2); 380 ContainerCreator creator = new ContainerCreator(fFile.getWorkspace(), fFile.getParent().getFullPath()); 381 IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 1); 382 creator.createContainer(subMonitor); 383 subMonitor.done(); 384 385 subMonitor= new SubProgressMonitor(monitor, 1); 386 fFile.create(stream, false, subMonitor); 387 subMonitor.done(); 388 389 } finally { 390 monitor.done(); 391 } 392 393 fSynchronizationStamp= fFile.getModificationStamp(); 395 396 } 398 399 } 400 401 private String computeEncoding() { 402 if (fExplicitEncoding != null) 404 return fExplicitEncoding; 405 406 Reader reader= new DocumentReader(fDocument); 408 try { 409 QualifiedName[] options= new QualifiedName[] { IContentDescription.CHARSET, IContentDescription.BYTE_ORDER_MARK }; 410 IContentDescription description= Platform.getContentTypeManager().getDescriptionFor(reader, fFile.getName(), options); 411 if (description != null) { 412 String encoding= description.getCharset(); 413 if (encoding != null) 414 return encoding; 415 } 416 } catch (IOException ex) { 417 } finally { 419 try { 420 if (reader != null) 421 reader.close(); 422 } catch (IOException x) { 423 } 424 } 425 426 if (fHasBOM) 428 return fEncoding; 429 430 try { 432 return fFile.getParent().getDefaultCharset(); 433 } catch (CoreException ex) { 434 return fManager.getDefaultEncoding(); 436 } 437 } 438 439 445 protected void cacheEncodingState() throws CoreException { 446 fExplicitEncoding= fFile.getCharset(false); 447 if (fExplicitEncoding != null) 448 fEncoding= fExplicitEncoding; 449 else 450 fEncoding= fFile.getCharset(); 451 setHasBOM(); 452 } 453 454 457 protected void handleFileContentChanged(boolean revert) throws CoreException { 458 459 IDocument document= fManager.createEmptyDocument(fFile); 460 IStatus status= null; 461 462 try { 463 cacheEncodingState(); 464 setDocumentContent(document, fFile, fEncoding); 465 } catch (CoreException x) { 466 status= x.getStatus(); 467 } 468 469 String newContent= document.get(); 470 boolean replaceContent= !newContent.equals(fDocument.get()); 471 472 if (replaceContent) 473 fManager.fireBufferContentAboutToBeReplaced(this); 474 475 removeFileBufferContentListeners(); 476 fSynchronizationStamp= fFile.getModificationStamp(); 477 if (replaceContent) { 478 if (fDocument instanceof IDocumentExtension4) 479 ((IDocumentExtension4)fDocument).set(newContent, fSynchronizationStamp); 480 else 481 fDocument.set(newContent); 482 } 483 fCanBeSaved= false; 484 fStatus= status; 485 addFileBufferContentListeners(); 486 487 if (replaceContent) 488 fManager.fireBufferContentReplaced(this); 489 490 if (fAnnotationModel instanceof IPersistableAnnotationModel) { 491 IPersistableAnnotationModel persistableModel= (IPersistableAnnotationModel) fAnnotationModel; 492 try { 493 if (revert) 494 persistableModel.revert(fDocument); 495 else 496 persistableModel.reinitialize(fDocument); 497 } catch (CoreException x) { 498 fStatus= x.getStatus(); 499 } 500 } 501 502 fManager.fireDirtyStateChanged(this, fCanBeSaved); 503 } 504 505 513 private void setDocumentContent(IDocument document, IFile file, String encoding) throws CoreException { 514 InputStream contentStream= file.getContents(); 515 Reader in= null; 516 try { 517 518 if (encoding == null) 519 encoding= fManager.getDefaultEncoding(); 520 521 526 if (fHasBOM && CHARSET_UTF_8.equals(encoding)) { 527 int n= 0; 528 do { 529 int bytes= contentStream.read(new byte[IContentDescription.BOM_UTF_8.length]); 530 if (bytes == -1) 531 throw new IOException (); 532 n += bytes; 533 } while (n < IContentDescription.BOM_UTF_8.length); 534 } 535 536 in= new BufferedReader (new InputStreamReader (contentStream, encoding), BUFFER_SIZE); 537 StringBuffer buffer= new StringBuffer (BUFFER_SIZE); 538 char[] readBuffer= new char[READER_CHUNK_SIZE]; 539 int n= in.read(readBuffer); 540 while (n > 0) { 541 buffer.append(readBuffer, 0, n); 542 n= in.read(readBuffer); 543 } 544 545 if (document instanceof IDocumentExtension4) 546 ((IDocumentExtension4)document).set(buffer.toString(), fFile.getModificationStamp()); 547 else 548 document.set(buffer.toString()); 549 550 } catch (IOException x) { 551 String message= (x.getMessage() != null ? x.getMessage() : ""); IStatus s= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, message, x); 553 throw new CoreException(s); 554 } finally { 555 try { 556 if (in != null) 557 in.close(); 558 else 559 contentStream.close(); 560 } catch (IOException x) { 561 } 562 } 563 } 564 } 565 | Popular Tags |