KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > filebuffers > JavaTextFileBuffer


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  *******************************************************************************/

11 package org.eclipse.core.internal.filebuffers;
12
13 import java.io.BufferedReader JavaDoc;
14 import java.io.ByteArrayInputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.InputStream JavaDoc;
17 import java.io.InputStreamReader JavaDoc;
18 import java.io.OutputStream JavaDoc;
19 import java.io.Reader JavaDoc;
20 import java.io.SequenceInputStream JavaDoc;
21 import java.nio.ByteBuffer JavaDoc;
22 import java.nio.CharBuffer JavaDoc;
23 import java.nio.charset.CharacterCodingException JavaDoc;
24 import java.nio.charset.Charset JavaDoc;
25 import java.nio.charset.CharsetEncoder JavaDoc;
26 import java.nio.charset.CodingErrorAction JavaDoc;
27 import java.nio.charset.IllegalCharsetNameException JavaDoc;
28 import java.nio.charset.UnmappableCharacterException JavaDoc;
29 import java.nio.charset.UnsupportedCharsetException JavaDoc;
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 /**
58  * @since 3.0
59  */

60 public class JavaTextFileBuffer extends JavaFileBuffer implements ITextFileBuffer {
61
62
63     private class DocumentListener implements IDocumentListener {
64
65         /*
66          * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
67          */

68         public void documentAboutToBeChanged(DocumentEvent event) {
69         }
70
71         /*
72          * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
73          */

74         public void documentChanged(DocumentEvent event) {
75             fCanBeSaved= true;
76             removeFileBufferContentListeners();
77             fManager.fireDirtyStateChanged(JavaTextFileBuffer.this, fCanBeSaved);
78         }
79     }
80
81     /**
82      * Reader chunk size.
83      */

84     private static final int READER_CHUNK_SIZE= 2048;
85     /**
86      * Buffer size.
87      */

88     private static final int BUFFER_SIZE= 8 * READER_CHUNK_SIZE;
89     /**
90      * Constant for representing the OK status. This is considered a value object.
91      */

92     private static final IStatus STATUS_OK= new Status(IStatus.OK, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.FileBuffer_status_ok, null);
93     /**
94      * Constant for representing the error status. This is considered a value object.
95      */

96     private static final IStatus STATUS_ERROR= new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, FileBuffersMessages.FileBuffer_status_error, null);
97     /**
98      * Constant denoting UTF-8 encoding.
99      */

100     private static final String JavaDoc CHARSET_UTF_8= "UTF-8"; //$NON-NLS-1$
101

102     /**
103      * Constant denoting an empty set of properties
104      * @since 3.1
105      */

106     private static final QualifiedName[] NO_PROPERTIES= new QualifiedName[0];
107
108
109     /** The element's document */
110     protected IDocument fDocument;
111     /** The encoding used to create the document from the storage or <code>null</code> for workbench encoding. */
112     protected String JavaDoc fEncoding;
113     /** Internal document listener */
114     protected IDocumentListener fDocumentListener= new DocumentListener();
115     /** The encoding which has explicitly been set on the file. */
116     private String JavaDoc fExplicitEncoding;
117     /** Tells whether the file on disk has a BOM. */
118     private boolean fHasBOM;
119     /** The annotation model of this file buffer */
120     private IAnnotationModel fAnnotationModel;
121     /**
122      * Lock for lazy creation of annotation model.
123      * @since 3.2
124      */

125     private final Object JavaDoc fAnnotationModelCreationLock= new Object JavaDoc();
126     /**
127      * Tells whether the cache is up to date.
128      * @since 3.2
129      */

130     private boolean fIsCacheUpdated= false;
131
132
133     public JavaTextFileBuffer(TextFileBufferManager manager) {
134         super(manager);
135     }
136
137     /*
138      * @see org.eclipse.core.filebuffers.ITextFileBuffer#getDocument()
139      */

140     public IDocument getDocument() {
141         return fDocument;
142     }
143
144     /*
145      * @see org.eclipse.core.filebuffers.ITextFileBuffer#getAnnotationModel()
146      */

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     /*
159      * @see org.eclipse.core.filebuffers.ITextFileBuffer#getEncoding()
160      */

161     public String JavaDoc getEncoding() {
162         if (!fIsCacheUpdated)
163             cacheEncodingState(null);
164         return fEncoding;
165     }
166
167     /*
168      * @see org.eclipse.core.filebuffers.ITextFileBuffer#setEncoding(java.lang.String)
169      */

170     public void setEncoding(String JavaDoc 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     /*
181      * @see org.eclipse.core.filebuffers.ITextFileBuffer#getStatus()
182      */

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 JavaDoc 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 JavaDoc stream, boolean overwrite, IProgressMonitor monitor) throws CoreException {
200         OutputStream JavaDoc 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 JavaDoc e) {
208                 }
209                 if (bytesRead == -1)
210                     break;
211                 try {
212                     out.write(buffer, 0, bytesRead);
213                 } catch (IOException JavaDoc e) {
214                 }
215                 if (monitor != null)
216                     monitor.worked(1);
217             }
218         } finally {
219             try {
220                 stream.close();
221             } catch (IOException JavaDoc e) {
222             } finally {
223                 try {
224                     out.close();
225                 } catch (IOException JavaDoc e) {
226                 }
227             }
228         }
229     }
230
231     /*
232      * @see org.eclipse.core.filebuffers.IFileBuffer#revert(org.eclipse.core.runtime.IProgressMonitor)
233      */

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 JavaDoc 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 JavaDoc x) {
291             fManager.fireStateChangeFailed(this);
292             throw x;
293         }
294     }
295
296     /*
297      * @see org.eclipse.core.filebuffers.IFileBuffer#getContentType()
298      * @since 3.1
299      */

300     public IContentType getContentType () throws CoreException {
301         if (fFileStore == null)
302             return null;
303
304         InputStream JavaDoc stream= null;
305         try {
306             if (isDirty()) {
307                 Reader JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc x) {
332             }
333         }
334     }
335
336     /*
337      * @see org.eclipse.core.internal.filebuffers.FileBuffer#addFileBufferContentListeners()
338      */

339     protected void addFileBufferContentListeners() {
340         if (fDocument != null)
341             fDocument.addDocumentListener(fDocumentListener);
342     }
343
344     /*
345      * @see org.eclipse.core.internal.filebuffers.FileBuffer#removeFileBufferContentListeners()
346      */

347     protected void removeFileBufferContentListeners() {
348         if (fDocument != null)
349             fDocument.removeDocumentListener(fDocumentListener);
350     }
351
352     /*
353      * @see org.eclipse.core.internal.filebuffers.FileBuffer#initializeFileBufferContent(org.eclipse.core.runtime.IProgressMonitor)
354      */

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     /*
367      * @see org.eclipse.core.internal.filebuffers.ResourceFileBuffer#connected()
368      */

369     protected void connected() {
370         super.connected();
371         if (fAnnotationModel != null)
372             fAnnotationModel.connect(fDocument);
373     }
374
375     /*
376      * @see org.eclipse.core.internal.filebuffers.ResourceFileBuffer#disconnected()
377      */

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 JavaDoc 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             // do nothing
404
} catch (IOException JavaDoc e) {
405             // do nothing
406
} finally {
407             try {
408                 if (stream != null)
409                     stream.close();
410             } catch (IOException JavaDoc 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     /*
417      * @see org.eclipse.core.internal.filebuffers.FileBuffer#commitFileBufferContent(org.eclipse.core.runtime.IProgressMonitor, boolean)
418      */

419     protected void commitFileBufferContent(IProgressMonitor monitor, boolean overwrite) throws CoreException {
420         String JavaDoc encoding= computeEncoding();
421
422         Charset JavaDoc charset;
423         try {
424             charset= Charset.forName(encoding);
425         } catch (UnsupportedCharsetException JavaDoc ex) {
426             String JavaDoc 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 JavaDoc ex) {
430             String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc ex) {
452             Assert.isTrue(ex instanceof UnmappableCharacterException JavaDoc);
453             String JavaDoc 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 JavaDoc stream= new ByteArrayInputStream JavaDoc(bytes, 0, bytesLength);
465
466             /*
467              * XXX:
468              * This is a workaround for a corresponding bug in Java readers and writer,
469              * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
470              */

471             if (fHasBOM && CHARSET_UTF_8.equals(encoding))
472                 stream= new SequenceInputStream JavaDoc(new ByteArrayInputStream JavaDoc(IContentDescription.BOM_UTF_8), stream);
473
474
475             // here the file synchronizer should actually be removed and afterwards added again. However,
476
// we are already inside an operation, so the delta is sent AFTER we have added the listener
477
setFileContents(stream, overwrite, monitor);
478             // set synchronization stamp to know whether the file synchronizer must become active
479
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 JavaDoc out= fFileStore.openOutputStream(EFS.NONE, null);
491             try {
492                 /*
493                  * XXX:
494                  * This is a workaround for a corresponding bug in Java readers and writer,
495                  * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
496                  */

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 JavaDoc 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 JavaDoc x) {
509                 }
510             }
511
512             // set synchronization stamp to know whether the file synchronizer must become active
513
fSynchronizationStamp= fFileStore.fetchInfo().getLastModified();
514
515         }
516     }
517
518     private String JavaDoc computeEncoding() {
519         // Make sure cache is up to date
520
if (!fIsCacheUpdated)
521             cacheEncodingState(null);
522         
523         // User-defined encoding has first priority
524
if (fExplicitEncoding != null)
525             return fExplicitEncoding;
526
527         if (fFileStore != null) {
528             // Probe content
529
Reader JavaDoc 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 JavaDoc encoding= description.getCharset();
535                     if (encoding != null)
536                         return encoding;
537                 }
538             } catch (IOException JavaDoc ex) {
539                 // try next strategy
540
} finally {
541                 try {
542                     if (reader != null)
543                         reader.close();
544                 } catch (IOException JavaDoc x) {
545                 }
546             }
547         }
548
549         // Use file's encoding if the file has a BOM
550
if (fHasBOM)
551             return fEncoding;
552
553         // Use global default
554
return fManager.getDefaultEncoding();
555     }
556
557     /**
558      * Initializes the given document with the given file's content using the given encoding.
559      *
560      * @param document the document to be initialized
561      * @param file the file which delivers the document content
562      * @param encoding the character encoding for reading the given stream
563      * @param hasBOM tell whether the given file has a BOM
564      * @param monitor the progress monitor
565      * @exception CoreException if the given stream can not be read
566      */

567     private void setDocumentContent(IDocument document, IFileStore file, String JavaDoc encoding, boolean hasBOM, IProgressMonitor monitor) throws CoreException {
568         InputStream JavaDoc contentStream= getFileContents(file, monitor);
569         if (contentStream == null)
570             return;
571
572         Reader JavaDoc in= null;
573         try {
574
575             if (encoding == null)
576                 encoding= fManager.getDefaultEncoding();
577
578             /*
579              * XXX:
580              * This is a workaround for a corresponding bug in Java readers and writer,
581              * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
582              */

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 JavaDoc();
589                     n += bytes;
590                 } while (n < IContentDescription.BOM_UTF_8.length);
591             }
592
593             in= new BufferedReader JavaDoc(new InputStreamReader JavaDoc(contentStream, encoding), BUFFER_SIZE);
594             StringBuffer JavaDoc buffer= new StringBuffer JavaDoc(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 JavaDoc x) {
605             String JavaDoc msg= x.getMessage() == null ? "" : x.getMessage(); //$NON-NLS-1$
606
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 JavaDoc x) {
615             }
616         }
617     }
618
619     /**
620      * Checks whether the given file is synchronized with the local file system.
621      * If the file has been changed, a <code>CoreException</code> is thrown.
622      *
623      * @exception CoreException if file has been changed on the file system
624      */

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