KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > editors > text > FileDocumentProvider


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.ui.editors.text;
12
13 import java.io.ByteArrayInputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.io.Reader JavaDoc;
17 import java.io.SequenceInputStream JavaDoc;
18 import java.nio.ByteBuffer JavaDoc;
19 import java.nio.CharBuffer JavaDoc;
20 import java.nio.charset.CharacterCodingException JavaDoc;
21 import java.nio.charset.Charset JavaDoc;
22 import java.nio.charset.CharsetEncoder JavaDoc;
23 import java.nio.charset.CodingErrorAction JavaDoc;
24 import java.nio.charset.IllegalCharsetNameException JavaDoc;
25 import java.nio.charset.UnmappableCharacterException JavaDoc;
26 import java.nio.charset.UnsupportedCharsetException JavaDoc;
27
28 import org.eclipse.swt.widgets.Display;
29
30 import org.eclipse.core.runtime.Assert;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IPath;
33 import org.eclipse.core.runtime.IProgressMonitor;
34 import org.eclipse.core.runtime.IStatus;
35 import org.eclipse.core.runtime.MultiStatus;
36 import org.eclipse.core.runtime.OperationCanceledException;
37 import org.eclipse.core.runtime.Platform;
38 import org.eclipse.core.runtime.QualifiedName;
39 import org.eclipse.core.runtime.Status;
40 import org.eclipse.core.runtime.SubProgressMonitor;
41 import org.eclipse.core.runtime.content.IContentDescription;
42 import org.eclipse.core.runtime.content.IContentType;
43 import org.eclipse.core.runtime.jobs.ISchedulingRule;
44 import org.eclipse.core.runtime.preferences.IScopeContext;
45 import org.eclipse.core.runtime.preferences.InstanceScope;
46
47 import org.eclipse.core.resources.IFile;
48 import org.eclipse.core.resources.IResource;
49 import org.eclipse.core.resources.IResourceChangeEvent;
50 import org.eclipse.core.resources.IResourceChangeListener;
51 import org.eclipse.core.resources.IResourceDelta;
52 import org.eclipse.core.resources.IResourceDeltaVisitor;
53 import org.eclipse.core.resources.IResourceRuleFactory;
54 import org.eclipse.core.resources.IResourceStatus;
55 import org.eclipse.core.resources.IWorkspace;
56 import org.eclipse.core.resources.ProjectScope;
57 import org.eclipse.core.resources.ResourcesPlugin;
58
59 import org.eclipse.core.filebuffers.manipulation.ContainerCreator;
60
61 import org.eclipse.jface.operation.IRunnableContext;
62
63 import org.eclipse.jface.text.IDocument;
64 import org.eclipse.jface.text.IDocumentExtension4;
65 import org.eclipse.jface.text.source.IAnnotationModel;
66
67 import org.eclipse.ui.IEditorInput;
68 import org.eclipse.ui.IFileEditorInput;
69 import org.eclipse.ui.IWorkbench;
70 import org.eclipse.ui.IWorkbenchWindow;
71 import org.eclipse.ui.PlatformUI;
72 import org.eclipse.ui.internal.editors.text.NLSUtility;
73 import org.eclipse.ui.internal.editors.text.WorkspaceOperationRunner;
74 import org.eclipse.ui.part.FileEditorInput;
75 import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
76 import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
77
78
79 /**
80  * Shared document provider specialized for file resources (<code>IFile</code>).
81  * <p>
82  * This class may be instantiated or be subclassed.</p>
83  */

84 public class FileDocumentProvider extends StorageDocumentProvider {
85
86     /**
87      * Qualified name for the encoding key.
88      *
89      * @since 2.1
90      */

91     private static final QualifiedName ENCODING_KEY = new QualifiedName(EditorsUI.PLUGIN_ID, "encoding"); //$NON-NLS-1$
92
/**
93      * Constant denoting UTF-8 encoding.
94      * @since 3.0
95      */

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

98
99     /**
100      * The runnable context for that provider.
101      * @since 3.0
102      */

103     private WorkspaceOperationRunner fOperationRunner;
104     /**
105      * The scheduling rule factory.
106      * @since 3.0
107      */

108     private IResourceRuleFactory fResourceRuleFactory;
109
110     /**
111      * Runnable encapsulating an element state change. This runnable ensures
112      * that a element change failed message is sent out to the element state listeners
113      * in case an exception occurred.
114      *
115      * @since 2.0
116      */

117     protected class SafeChange implements Runnable JavaDoc {
118
119         /** The input that changes. */
120         private IFileEditorInput fInput;
121
122         /**
123          * Creates a new safe runnable for the given input.
124          *
125          * @param input the input
126          */

127         public SafeChange(IFileEditorInput input) {
128             fInput= input;
129         }
130
131         /**
132          * Execute the change.
133          * Subclass responsibility.
134          *
135          * @param input the input
136          * @throws Exception an exception in case of error
137          */

138         protected void execute(IFileEditorInput input) throws Exception JavaDoc {
139         }
140
141         /*
142          * @see java.lang.Runnable#run()
143          */

144         public void run() {
145
146             if (getElementInfo(fInput) == null) {
147                 fireElementStateChangeFailed(fInput);
148                 return;
149             }
150
151             try {
152                 execute(fInput);
153             } catch (Exception JavaDoc e) {
154                 fireElementStateChangeFailed(fInput);
155             }
156         }
157     }
158
159
160     /**
161      * Synchronizes the document with external resource changes.
162      */

163     protected class FileSynchronizer implements IResourceChangeListener, IResourceDeltaVisitor {
164
165         /** The file editor input. */
166         protected IFileEditorInput fFileEditorInput;
167         /**
168          * A flag indicating whether this synchronizer is installed or not.
169          *
170          * @since 2.1
171          */

172         protected boolean fIsInstalled= false;
173
174         /**
175          * Creates a new file synchronizer. Is not yet installed on a resource.
176          *
177          * @param fileEditorInput the editor input to be synchronized
178          */

179         public FileSynchronizer(IFileEditorInput fileEditorInput) {
180             fFileEditorInput= fileEditorInput;
181         }
182
183         /**
184          * Creates a new file synchronizer which is not yet installed on a resource.
185          *
186          * @param fileEditorInput the editor input to be synchronized
187          * @deprecated use <code>FileSynchronizer(IFileEditorInput)</code>
188          */

189         public FileSynchronizer(FileEditorInput fileEditorInput) {
190             fFileEditorInput= fileEditorInput;
191         }
192
193         /**
194          * Returns the file wrapped by the file editor input.
195          *
196          * @return the file wrapped by the editor input associated with that synchronizer
197          */

198         protected IFile getFile() {
199             return fFileEditorInput.getFile();
200         }
201
202         /**
203          * Installs the synchronizer on the input's file.
204          */

205         public void install() {
206             getFile().getWorkspace().addResourceChangeListener(this);
207             fIsInstalled= true;
208         }
209
210         /**
211          * Uninstalls the synchronizer from the input's file.
212          */

213         public void uninstall() {
214             getFile().getWorkspace().removeResourceChangeListener(this);
215             fIsInstalled= false;
216         }
217
218         /*
219          * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
220          */

221         public void resourceChanged(IResourceChangeEvent e) {
222             IResourceDelta delta= e.getDelta();
223             try {
224                 if (delta != null && fIsInstalled)
225                     delta.accept(this);
226             } catch (CoreException x) {
227                 handleCoreException(x, "FileDocumentProvider.resourceChanged"); //$NON-NLS-1$
228
}
229         }
230
231         /*
232          * @see IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
233          */

234         public boolean visit(IResourceDelta delta) throws CoreException {
235             if (delta == null)
236                 return false;
237
238             delta= delta.findMember(getFile().getFullPath());
239
240             if (delta == null)
241                 return false;
242
243             Runnable JavaDoc runnable= null;
244
245             switch (delta.getKind()) {
246                 case IResourceDelta.CHANGED:
247                     FileInfo info= (FileInfo) getElementInfo(fFileEditorInput);
248                     if (info == null || info.fCanBeSaved)
249                         break;
250
251                     boolean isSynchronized= computeModificationStamp(getFile()) == info.fModificationStamp;
252                     if ((IResourceDelta.ENCODING & delta.getFlags()) != 0 && isSynchronized) {
253                         runnable= new SafeChange(fFileEditorInput) {
254                             protected void execute(IFileEditorInput input) throws Exception JavaDoc {
255                                 handleElementContentChanged(input);
256                             }
257                         };
258                     }
259
260                     if (runnable == null && (IResourceDelta.CONTENT & delta.getFlags()) != 0 && !isSynchronized) {
261                         runnable= new SafeChange(fFileEditorInput) {
262                             protected void execute(IFileEditorInput input) throws Exception JavaDoc {
263                                 handleElementContentChanged(input);
264                             }
265                         };
266                     }
267                     break;
268
269                 case IResourceDelta.REMOVED:
270                     if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) {
271                         final IPath path= delta.getMovedToPath();
272                         runnable= new SafeChange(fFileEditorInput) {
273                             protected void execute(IFileEditorInput input) throws Exception JavaDoc {
274                                 handleElementMoved(input, path);
275                             }
276                         };
277                     } else {
278                         info= (FileInfo) getElementInfo(fFileEditorInput);
279                         if (info != null && !info.fCanBeSaved) {
280                             runnable= new SafeChange(fFileEditorInput) {
281                                 protected void execute(IFileEditorInput input) throws Exception JavaDoc {
282                                     handleElementDeleted(input);
283                                 }
284                             };
285                         }
286                     }
287                     break;
288             }
289
290             if (runnable != null)
291                 update(runnable);
292
293             return false;
294         }
295
296         /**
297          * Posts the update code "behind" the running operation.
298          *
299          * @param runnable the update code
300          */

301         protected void update(Runnable JavaDoc runnable) {
302
303             if (runnable instanceof SafeChange)
304                 fireElementStateChanging(fFileEditorInput);
305
306             IWorkbench workbench= PlatformUI.getWorkbench();
307             IWorkbenchWindow[] windows= workbench.getWorkbenchWindows();
308             if (windows != null && windows.length > 0) {
309                 Display display= windows[0].getShell().getDisplay();
310                 display.asyncExec(runnable);
311             } else {
312                 runnable.run();
313             }
314         }
315     }
316
317
318
319     /**
320      * Bundle of all required information to allow files as underlying document resources.
321      */

322     protected class FileInfo extends StorageInfo {
323
324         /** The file synchronizer. */
325         public FileSynchronizer fFileSynchronizer;
326         /** The time stamp at which this provider changed the file. */
327         public long fModificationStamp= IResource.NULL_STAMP;
328         /**
329          * Tells whether the file on disk has a BOM.
330          */

331         private boolean fHasBOM;
332
333         /**
334          * Creates and returns a new file info.
335          *
336          * @param document the document
337          * @param model the annotation model
338          * @param fileSynchronizer the file synchronizer
339          */

340         public FileInfo(IDocument document, IAnnotationModel model, FileSynchronizer fileSynchronizer) {
341             super(document, model);
342             fFileSynchronizer= fileSynchronizer;
343         }
344     }
345
346
347     /**
348      * Creates and returns a new document provider.
349      */

350     public FileDocumentProvider() {
351         super();
352         fResourceRuleFactory= ResourcesPlugin.getWorkspace().getRuleFactory();
353     }
354
355     /**
356      * Overrides <code>StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput)</code>.
357      *
358      * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput)
359      * @deprecated use file encoding based version
360      * @since 2.0
361      */

362     protected boolean setDocumentContent(IDocument document, IEditorInput editorInput) throws CoreException {
363         if (editorInput instanceof IFileEditorInput) {
364             IFile file= ((IFileEditorInput) editorInput).getFile();
365             InputStream JavaDoc stream= file.getContents(false);
366             try {
367                 setDocumentContent(document, stream);
368             } finally {
369                 try {
370                     stream.close();
371                 } catch (IOException JavaDoc x) {
372                 }
373             }
374             return true;
375         }
376         return super.setDocumentContent(document, editorInput);
377     }
378
379     /*
380      * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput, String)
381      * @since 2.0
382      */

383     protected boolean setDocumentContent(IDocument document, IEditorInput editorInput, String JavaDoc encoding) throws CoreException {
384         if (editorInput instanceof IFileEditorInput) {
385             IFile file= ((IFileEditorInput) editorInput).getFile();
386             InputStream JavaDoc contentStream= file.getContents(false);
387             try {
388
389                 FileInfo info= (FileInfo)getElementInfo(editorInput);
390                 boolean removeBOM= false;
391                 if (CHARSET_UTF_8.equals(encoding)) {
392                     if (info != null)
393                         removeBOM= info.fHasBOM;
394                     else
395                         removeBOM= hasBOM(editorInput);
396                 }
397
398                 /*
399                  * XXX:
400                  * This is a workaround for a corresponding bug in Java readers and writer,
401                  * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
402                  */

403                 if (removeBOM) {
404                     int n= 0;
405                     do {
406                         int bytes= contentStream.read(new byte[IContentDescription.BOM_UTF_8.length]);
407                         if (bytes == -1)
408                             throw new IOException JavaDoc();
409                         n += bytes;
410                     } while (n < IContentDescription.BOM_UTF_8.length);
411                 }
412
413                 setDocumentContent(document, contentStream, encoding);
414
415             } catch (IOException JavaDoc ex) {
416                 String JavaDoc message= (ex.getMessage() != null ? ex.getMessage() : ""); //$NON-NLS-1$
417
IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, message, ex);
418                 throw new CoreException(s);
419             } finally {
420                 try {
421                     contentStream.close();
422                 } catch (IOException JavaDoc e1) {
423                 }
424             }
425             return true;
426         }
427         return super.setDocumentContent(document, editorInput, encoding);
428     }
429
430     /*
431      * @see AbstractDocumentProvider#createAnnotationModel(Object)
432      */

433     protected IAnnotationModel createAnnotationModel(Object JavaDoc element) throws CoreException {
434         if (element instanceof IFileEditorInput) {
435             IFileEditorInput input= (IFileEditorInput) element;
436             return new ResourceMarkerAnnotationModel(input.getFile());
437         }
438
439         return super.createAnnotationModel(element);
440     }
441
442     /**
443      * Checks whether the given resource has been changed on the
444      * local file system by comparing the actual time stamp with the
445      * cached one. If the resource has been changed, a <code>CoreException</code>
446      * is thrown.
447      *
448      * @param cachedModificationStamp the cached modification stamp
449      * @param resource the resource to check
450      * @throws org.eclipse.core.runtime.CoreException if resource has been changed on the file system
451      */

452     protected void checkSynchronizationState(long cachedModificationStamp, IResource resource) throws CoreException {
453         if (cachedModificationStamp != computeModificationStamp(resource)) {
454             Status status= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IResourceStatus.OUT_OF_SYNC_LOCAL, TextEditorMessages.FileDocumentProvider_error_out_of_sync, null);
455             throw new CoreException(status);
456         }
457     }
458
459     /**
460      * Computes the initial modification stamp for the given resource.
461      *
462      * @param resource the resource
463      * @return the modification stamp
464      */

465     protected long computeModificationStamp(IResource resource) {
466         long modificationStamp= resource.getModificationStamp();
467
468         IPath path= resource.getLocation();
469         if (path == null)
470             return modificationStamp;
471
472         modificationStamp= path.toFile().lastModified();
473         return modificationStamp;
474     }
475
476     /*
477      * @see IDocumentProvider#getModificationStamp(Object)
478      */

479     public long getModificationStamp(Object JavaDoc element) {
480
481         if (element instanceof IFileEditorInput) {
482             IFileEditorInput input= (IFileEditorInput) element;
483             return computeModificationStamp(input.getFile());
484         }
485
486         return super.getModificationStamp(element);
487     }
488
489     /*
490      * @see IDocumentProvider#getSynchronizationStamp(Object)
491      */

492     public long getSynchronizationStamp(Object JavaDoc element) {
493
494         if (element instanceof IFileEditorInput) {
495             FileInfo info= (FileInfo) getElementInfo(element);
496             if (info != null)
497                 return info.fModificationStamp;
498         }
499
500         return super.getSynchronizationStamp(element);
501     }
502
503     /*
504      * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#doSynchronize(java.lang.Object)
505      * @since 3.0
506      */

507     protected void doSynchronize(Object JavaDoc element, IProgressMonitor monitor) throws CoreException {
508         if (element instanceof IFileEditorInput) {
509
510             IFileEditorInput input= (IFileEditorInput) element;
511
512             FileInfo info= (FileInfo) getElementInfo(element);
513             if (info != null) {
514
515                 if (info.fFileSynchronizer != null) {
516                     info.fFileSynchronizer.uninstall();
517                     refreshFile(input.getFile(), monitor);
518                     info.fFileSynchronizer.install();
519                 } else {
520                     refreshFile(input.getFile(), monitor);
521                 }
522
523                 handleElementContentChanged((IFileEditorInput) element);
524             }
525             return;
526
527         }
528         super.doSynchronize(element, monitor);
529     }
530
531     /*
532      * @see IDocumentProvider#isDeleted(Object)
533      */

534     public boolean isDeleted(Object JavaDoc element) {
535
536         if (element instanceof IFileEditorInput) {
537             IFileEditorInput input= (IFileEditorInput) element;
538
539             IPath path= input.getFile().getLocation();
540             if (path == null)
541                 return true;
542
543             return !path.toFile().exists();
544         }
545
546         return super.isDeleted(element);
547     }
548
549     /*
550      * @see AbstractDocumentProvider#doSaveDocument(IProgressMonitor, Object, IDocument, boolean)
551      */

552     protected void doSaveDocument(IProgressMonitor monitor, Object JavaDoc element, IDocument document, boolean overwrite) throws CoreException {
553         if (element instanceof IFileEditorInput) {
554
555             IFileEditorInput input= (IFileEditorInput) element;
556             String JavaDoc encoding= null;
557
558             FileInfo info= (FileInfo) getElementInfo(element);
559             IFile file= input.getFile();
560             encoding= getCharsetForNewFile(file, document, info);
561
562             Charset JavaDoc charset;
563             try {
564                 charset= Charset.forName(encoding);
565             } catch (UnsupportedCharsetException JavaDoc ex) {
566                 String JavaDoc message= NLSUtility.format(TextEditorMessages.DocumentProvider_error_unsupported_encoding_message_arg, encoding);
567                 IStatus s= new Status(IStatus.ERROR, EditorsUI.PLUGIN_ID, IStatus.OK, message, ex);
568                 throw new CoreException(s);
569             } catch (IllegalCharsetNameException JavaDoc ex) {
570                 String JavaDoc message= NLSUtility.format(TextEditorMessages.DocumentProvider_error_illegal_encoding_message_arg, encoding);
571                 IStatus s= new Status(IStatus.ERROR, EditorsUI.PLUGIN_ID, IStatus.OK, message, ex);
572                 throw new CoreException(s);
573             }
574
575             CharsetEncoder JavaDoc encoder= charset.newEncoder();
576             encoder.onMalformedInput(CodingErrorAction.REPLACE);
577             encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
578
579             InputStream JavaDoc stream;
580
581             try {
582                 byte[] bytes;
583                 ByteBuffer JavaDoc byteBuffer= encoder.encode(CharBuffer.wrap(document.get()));
584                 if (byteBuffer.hasArray())
585                     bytes= byteBuffer.array();
586                 else {
587                     bytes= new byte[byteBuffer.limit()];
588                     byteBuffer.get(bytes);
589                 }
590                 stream= new ByteArrayInputStream JavaDoc(bytes, 0, byteBuffer.limit());
591             } catch (CharacterCodingException JavaDoc ex) {
592                 Assert.isTrue(ex instanceof UnmappableCharacterException JavaDoc);
593                 String JavaDoc message= NLSUtility.format(TextEditorMessages.DocumentProvider_error_charset_mapping_failed_message_arg, encoding);
594                 IStatus s= new Status(IStatus.ERROR, EditorsUI.PLUGIN_ID, EditorsUI.CHARSET_MAPPING_FAILED, message, null);
595                 throw new CoreException(s);
596             }
597
598             /*
599              * XXX:
600              * This is a workaround for a corresponding bug in Java readers and writer,
601              * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
602              */

603             if (info != null && info.fHasBOM && CHARSET_UTF_8.equals(encoding))
604                 stream= new SequenceInputStream JavaDoc(new ByteArrayInputStream JavaDoc(IContentDescription.BOM_UTF_8), stream);
605
606             if (file.exists()) {
607
608                 if (info != null && !overwrite)
609                     checkSynchronizationState(info.fModificationStamp, file);
610
611                 // inform about the upcoming content change
612
fireElementStateChanging(element);
613                 try {
614                     file.setContents(stream, overwrite, true, monitor);
615                 } catch (CoreException x) {
616                     // inform about failure
617
fireElementStateChangeFailed(element);
618                     throw x;
619                 } catch (RuntimeException JavaDoc x) {
620                     // inform about failure
621
fireElementStateChangeFailed(element);
622                     throw x;
623                 }
624
625                 // If here, the editor state will be flipped to "not dirty".
626
// Thus, the state changing flag will be reset.
627

628                 if (info != null) {
629
630                     ResourceMarkerAnnotationModel model= (ResourceMarkerAnnotationModel) info.fModel;
631                     if (model != null)
632                         model.updateMarkers(info.fDocument);
633
634                     info.fModificationStamp= computeModificationStamp(file);
635                 }
636
637             } else {
638                 try {
639                     monitor.beginTask(TextEditorMessages.FileDocumentProvider_task_saving, 2000);
640                     ContainerCreator creator = new ContainerCreator(file.getWorkspace(), file.getParent().getFullPath());
641                     creator.createContainer(new SubProgressMonitor(monitor, 1000));
642                     file.create(stream, false, new SubProgressMonitor(monitor, 1000));
643                 }
644                 finally {
645                     monitor.done();
646                 }
647             }
648
649         } else {
650             super.doSaveDocument(monitor, element, document, overwrite);
651         }
652     }
653
654     /*
655      * @since 3.0
656      */

657     private String JavaDoc getCharsetForNewFile(IFile targetFile, IDocument document, FileInfo info) {
658         // User-defined encoding has first priority
659
String JavaDoc encoding;
660         try {
661             encoding= targetFile.getCharset(false);
662         } catch (CoreException ex) {
663             encoding= null;
664         }
665         if (encoding != null)
666             return encoding;
667
668         // Probe content
669
Reader JavaDoc reader= new DocumentReader(document);
670         try {
671             QualifiedName[] options= new QualifiedName[] { IContentDescription.CHARSET, IContentDescription.BYTE_ORDER_MARK };
672             IContentDescription description= Platform.getContentTypeManager().getDescriptionFor(reader, targetFile.getName(), options);
673             if (description != null) {
674                 encoding= description.getCharset();
675                 if (encoding != null)
676                     return encoding;
677             }
678         } catch (IOException JavaDoc ex) {
679             // continue with next strategy
680
} finally {
681             try {
682                 reader.close();
683             } catch (IOException JavaDoc x) {
684             }
685         }
686
687         // Use file's encoding if the file has a BOM
688
if (info != null && info.fHasBOM)
689             return info.fEncoding;
690
691         // Use parent chain
692
try {
693             return targetFile.getParent().getDefaultCharset();
694         } catch (CoreException ex) {
695             // Use global default
696
return ResourcesPlugin.getEncoding();
697         }
698     }
699
700     /*
701      * @see AbstractDocumentProvider#createElementInfo(Object)
702      */

703     protected ElementInfo createElementInfo(Object JavaDoc element) throws CoreException {
704         if (element instanceof IFileEditorInput) {
705
706             IFileEditorInput input= (IFileEditorInput) element;
707
708             try {
709                 refreshFile(input.getFile());
710             } catch (CoreException x) {
711                 handleCoreException(x, TextEditorMessages.FileDocumentProvider_createElementInfo);
712             }
713
714             IDocument d= null;
715             IStatus s= null;
716
717             try {
718                 d= createDocument(element);
719             } catch (CoreException x) {
720                 handleCoreException(x, TextEditorMessages.FileDocumentProvider_createElementInfo);
721                 s= x.getStatus();
722                 d= createEmptyDocument();
723             }
724             
725             // Set the initial line delimiter
726
if (d instanceof IDocumentExtension4) {
727                 String JavaDoc initalLineDelimiter= getLineDelimiterPreference(input.getFile());
728                 if (initalLineDelimiter != null)
729                     ((IDocumentExtension4)d).setInitialLineDelimiter(initalLineDelimiter);
730             }
731
732             IAnnotationModel m= createAnnotationModel(element);
733             FileSynchronizer f= new FileSynchronizer(input);
734             f.install();
735
736             FileInfo info= new FileInfo(d, m, f);
737             info.fModificationStamp= computeModificationStamp(input.getFile());
738             info.fStatus= s;
739             info.fEncoding= getPersistedEncoding(element);
740             info.fHasBOM= hasBOM(element);
741
742             /*
743              * The code below is a no-op in the implementation in this class
744              * because the info is not yet stored in the element map.
745              * Calling to not break clients who have overridden the method.
746              * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=109255
747              */

748             cacheEncodingState(element);
749
750             return info;
751         }
752
753         return super.createElementInfo(element);
754     }
755     
756     /**
757      * Returns the default line delimiter preference for the given file.
758      *
759      * @param file the file
760      * @return the default line delimiter
761      * @since 3.1
762      */

763     private String JavaDoc getLineDelimiterPreference(IFile file) {
764         IScopeContext[] scopeContext;
765         if (file != null && file.getProject() != null) {
766             // project preference
767
scopeContext= new IScopeContext[] { new ProjectScope(file.getProject()) };
768             String JavaDoc lineDelimiter= Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
769             if (lineDelimiter != null)
770                 return lineDelimiter;
771         }
772         // workspace preference
773
scopeContext= new IScopeContext[] { new InstanceScope() };
774         return Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
775     }
776
777     /*
778      * @see AbstractDocumentProvider#disposeElementInfo(Object, ElementInfo)
779      */

780     protected void disposeElementInfo(Object JavaDoc element, ElementInfo info) {
781         if (info instanceof FileInfo) {
782             FileInfo fileInfo= (FileInfo) info;
783             if (fileInfo.fFileSynchronizer != null)
784                 fileInfo.fFileSynchronizer.uninstall();
785         }
786
787         super.disposeElementInfo(element, info);
788     }
789
790     /**
791      * Updates the element info to a change of the file content and sends out
792      * appropriate notifications.
793      *
794      * @param fileEditorInput the input of an text editor
795      */

796     protected void handleElementContentChanged(IFileEditorInput fileEditorInput) {
797         FileInfo info= (FileInfo) getElementInfo(fileEditorInput);
798         if (info == null)
799             return;
800
801         IDocument document= createEmptyDocument();
802         IStatus status= null;
803
804         try {
805
806             try {
807                 refreshFile(fileEditorInput.getFile());
808             } catch (CoreException x) {
809                 handleCoreException(x, "FileDocumentProvider.handleElementContentChanged"); //$NON-NLS-1$
810
}
811
812             cacheEncodingState(fileEditorInput);
813             setDocumentContent(document, fileEditorInput, info.fEncoding);
814
815         } catch (CoreException x) {
816             status= x.getStatus();
817         }
818
819         String JavaDoc newContent= document.get();
820
821         if ( !newContent.equals(info.fDocument.get())) {
822
823             // set the new content and fire content related events
824
fireElementContentAboutToBeReplaced(fileEditorInput);
825
826             removeUnchangedElementListeners(fileEditorInput, info);
827
828             info.fDocument.removeDocumentListener(info);
829             info.fDocument.set(newContent);
830             info.fCanBeSaved= false;
831             info.fModificationStamp= computeModificationStamp(fileEditorInput.getFile());
832             info.fStatus= status;
833
834             addUnchangedElementListeners(fileEditorInput, info);
835
836             fireElementContentReplaced(fileEditorInput);
837
838         } else {
839
840             removeUnchangedElementListeners(fileEditorInput, info);
841
842             // fires only the dirty state related event
843
info.fCanBeSaved= false;
844             info.fModificationStamp= computeModificationStamp(fileEditorInput.getFile());
845             info.fStatus= status;
846
847             addUnchangedElementListeners(fileEditorInput, info);
848
849             fireElementDirtyStateChanged(fileEditorInput, false);
850         }
851     }
852
853     /**
854      * Sends out the notification that the file serving as document input has been moved.
855      *
856      * @param fileEditorInput the input of an text editor
857      * @param path the path of the new location of the file
858      */

859     protected void handleElementMoved(IFileEditorInput fileEditorInput, IPath path) {
860         IWorkspace workspace= ResourcesPlugin.getWorkspace();
861         IFile newFile= workspace.getRoot().getFile(path);
862         fireElementMoved(fileEditorInput, newFile == null ? null : new FileEditorInput(newFile));
863     }
864
865     /**
866      * Sends out the notification that the file serving as document input has been deleted.
867      *
868      * @param fileEditorInput the input of an text editor
869      */

870     protected void handleElementDeleted(IFileEditorInput fileEditorInput) {
871         fireElementDeleted(fileEditorInput);
872     }
873
874     /*
875      * @see AbstractDocumentProvider#getElementInfo(Object)
876      * It's only here to circumvent visibility issues with certain compilers.
877      */

878     protected ElementInfo getElementInfo(Object JavaDoc element) {
879         return super.getElementInfo(element);
880     }
881
882     /*
883      * @see AbstractDocumentProvider#doValidateState(Object, Object)
884      * @since 2.0
885      */

886     protected void doValidateState(Object JavaDoc element, Object JavaDoc computationContext) throws CoreException {
887
888         if (element instanceof IFileEditorInput) {
889             IFileEditorInput input= (IFileEditorInput) element;
890             FileInfo info= (FileInfo) getElementInfo(input);
891             if (info != null) {
892                 IFile file= input.getFile();
893                 if (file.isReadOnly()) { // do not use cached state here
894
IWorkspace workspace= file.getWorkspace();
895                     info.fStatus= workspace.validateEdit(new IFile[] { file }, computationContext);
896                 }
897                 if (isDerived(file)) {
898                     IStatus status= new Status(IStatus.WARNING, EditorsUI.PLUGIN_ID, EditorsUI.DERIVED_FILE, TextEditorMessages.FileDocumentProvider_warning_fileIsDerived, null);
899                     if (info.fStatus == null || info.fStatus.isOK())
900                         info.fStatus= status;
901                     else
902                         info.fStatus= new MultiStatus(EditorsUI.PLUGIN_ID, EditorsUI.STATE_VALIDATION_FAILED, new IStatus[] {info.fStatus, status}, TextEditorMessages.FileDocumentProvider_stateValidationFailed, null);
903                 }
904             }
905         }
906
907         super.doValidateState(element, computationContext);
908     }
909     
910     /*
911      *
912      * @see IResource#isDerived()
913      * @since 3.3
914      */

915     private boolean isDerived(IResource resource) {
916         while (resource != null) {
917             if (resource.isDerived())
918                 return true;
919             resource= resource.getParent();
920         }
921         return false;
922     }
923
924     /*
925      * @see IDocumentProviderExtension#isModifiable(Object)
926      * @since 2.0
927      */

928     public boolean isModifiable(Object JavaDoc element) {
929         if (!isStateValidated(element)) {
930             if (element instanceof IFileEditorInput)
931                 return true;
932         }
933         return super.isModifiable(element);
934     }
935
936     /*
937      * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#doResetDocument(java.lang.Object, org.eclipse.core.runtime.IProgressMonitor)
938      * @since 3.0
939      */

940     protected void doResetDocument(Object JavaDoc element, IProgressMonitor monitor) throws CoreException {
941         if (element instanceof IFileEditorInput) {
942             IFileEditorInput input= (IFileEditorInput) element;
943             try {
944                 refreshFile(input.getFile(), monitor);
945                 cacheEncodingState(element);
946             } catch (CoreException x) {
947                 handleCoreException(x,TextEditorMessages.FileDocumentProvider_resetDocument);
948             }
949         }
950
951         super.doResetDocument(element, monitor);
952
953         IAnnotationModel model= getAnnotationModel(element);
954         if (model instanceof AbstractMarkerAnnotationModel) {
955             AbstractMarkerAnnotationModel markerModel= (AbstractMarkerAnnotationModel) model;
956             markerModel.resetMarkers();
957         }
958     }
959
960     /**
961      * Refreshes the given file resource.
962      *
963      * @param file
964      * @throws CoreException if the refresh fails
965      * @since 2.1
966      */

967     protected void refreshFile(IFile file) throws CoreException {
968         refreshFile(file, getProgressMonitor());
969     }
970
971     /**
972      * Refreshes the given file resource.
973      *
974      * @param file the file to be refreshed
975      * @param monitor the progress monitor
976      * @throws org.eclipse.core.runtime.CoreException if the refresh fails
977      * @since 3.0
978      */

979     protected void refreshFile(IFile file, IProgressMonitor monitor) throws CoreException {
980         try {
981             file.refreshLocal(IResource.DEPTH_INFINITE, monitor);
982         } catch (OperationCanceledException x) {
983         }
984     }
985
986     /*
987      * @see org.eclipse.ui.texteditor.IDocumentProviderExtension3#isSynchronized(java.lang.Object)
988      * @since 3.0
989      */

990     public boolean isSynchronized(Object JavaDoc element) {
991         if (element instanceof IFileEditorInput) {
992             if (getElementInfo(element) != null) {
993                 IFileEditorInput input= (IFileEditorInput) element;
994                 IResource resource= input.getFile();
995                 return resource.isSynchronized(IResource.DEPTH_ZERO);
996             }
997             return false;
998         }
999         return super.isSynchronized(element);
1000    }
1001
1002    /*
1003     * @see org.eclipse.ui.texteditor.IDocumentProviderExtension4#getContentType(java.lang.Object)
1004     * @since 3.1
1005     */

1006    public IContentType getContentType(Object JavaDoc element) throws CoreException {
1007        IContentType contentType= null;
1008        if (!canSaveDocument(element) && element instanceof IFileEditorInput)
1009            contentType= getContentType((IFileEditorInput) element);
1010
1011        if (contentType == null)
1012            contentType= super.getContentType(element);
1013
1014        if (contentType == null && element instanceof IFileEditorInput)
1015            contentType= getContentType((IFileEditorInput) element);
1016
1017        return contentType;
1018    }
1019
1020    /**
1021     * Returns the content type of for the given file editor input or
1022     * <code>null</code> if none could be determined.
1023     *
1024     * @param input the element
1025     * @return the content type or <code>null</code>
1026     * @throws CoreException if reading or accessing the underlying store
1027     * fails
1028     * @since 3.1
1029     */

1030    private IContentType getContentType(IFileEditorInput input) throws CoreException {
1031        IContentDescription desc= input.getFile().getContentDescription();
1032        if (desc != null)
1033            return desc.getContentType();
1034        return null;
1035    }
1036
1037    // --------------- Encoding support ---------------
1038

1039    /**
1040     * Returns the persisted encoding for the given element.
1041     *
1042     * @param element the element for which to get the persisted encoding
1043     * @return the persisted encoding
1044     * @since 2.1
1045     */

1046    protected String JavaDoc getPersistedEncoding(Object JavaDoc element) {
1047        if (element instanceof IFileEditorInput) {
1048            IFileEditorInput editorInput= (IFileEditorInput)element;
1049            IFile file= editorInput.getFile();
1050            if (file != null) {
1051                String JavaDoc encoding= null;
1052                try {
1053                    encoding= file.getPersistentProperty(ENCODING_KEY);
1054                } catch (CoreException x) {
1055                    // we ignore exceptions here because we support the ENCODING_KEY property only for compatibility reasons
1056
}
1057                if (encoding != null) {
1058                    // if we found an old encoding property, we try to migrate it to the new core.resources encoding support
1059
try {
1060                        file.setCharset(encoding, getProgressMonitor());
1061                        // if successful delete old property
1062
file.setPersistentProperty(ENCODING_KEY, null);
1063                    } catch (CoreException ex) {
1064                        handleCoreException(ex, TextEditorMessages.FileDocumentProvider_getPersistedEncoding);
1065                    }
1066                } else {
1067                    try {
1068                        encoding= file.getCharset();
1069                    } catch (CoreException e) {
1070                        return null;
1071                    }
1072                }
1073                return encoding;
1074            }
1075        }
1076        return super.getPersistedEncoding(element);
1077    }
1078
1079    /**
1080     * Persists the given encoding for the given element.
1081     *
1082     * @param element the element for which to store the persisted encoding
1083     * @param encoding the encoding
1084     * @throws org.eclipse.core.runtime.CoreException if persisting the encoding fails
1085     * @since 2.1
1086     */

1087    protected void persistEncoding(Object JavaDoc element, String JavaDoc encoding) throws CoreException {
1088        if (element instanceof IFileEditorInput) {
1089            IFileEditorInput editorInput= (IFileEditorInput)element;
1090            IFile file= editorInput.getFile();
1091            if (file != null) {
1092                file.setCharset(encoding, getProgressMonitor());
1093                StorageInfo info= (StorageInfo)getElementInfo(element);
1094                if (info != null) {
1095                    if (encoding == null)
1096                        info.fEncoding= file.getCharset();
1097                    if (info instanceof FileInfo)
1098                        ((FileInfo)info).fHasBOM= hasBOM(element);
1099                }
1100            }
1101        }
1102    }
1103
1104    /*
1105     * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getOperationRunner(org.eclipse.core.runtime.IProgressMonitor)
1106     * @since 3.0
1107     */

1108    protected IRunnableContext getOperationRunner(IProgressMonitor monitor) {
1109        if (fOperationRunner == null)
1110            fOperationRunner = new WorkspaceOperationRunner();
1111        fOperationRunner.setProgressMonitor(monitor);
1112        return fOperationRunner;
1113    }
1114
1115    /*
1116     * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getResetRule(java.lang.Object)
1117     * @since 3.0
1118     */

1119    protected ISchedulingRule getResetRule(Object JavaDoc element) {
1120        if (element instanceof IFileEditorInput) {
1121            IFileEditorInput input= (IFileEditorInput) element;
1122            return fResourceRuleFactory.refreshRule(input.getFile());
1123        }
1124        return null;
1125    }
1126
1127    /*
1128     * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getSaveRule(java.lang.Object)
1129     * @since 3.0
1130     */

1131    protected ISchedulingRule getSaveRule(Object JavaDoc element) {
1132        if (element instanceof IFileEditorInput) {
1133            IFileEditorInput input= (IFileEditorInput) element;
1134            return computeSchedulingRule(input.getFile());
1135        }
1136        return null;
1137    }
1138
1139    /*
1140     * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getSynchronizeRule(java.lang.Object)
1141     * @since 3.0
1142     */

1143    protected ISchedulingRule getSynchronizeRule(Object JavaDoc element) {
1144        if (element instanceof IFileEditorInput) {
1145            IFileEditorInput input= (IFileEditorInput) element;
1146            return fResourceRuleFactory.refreshRule(input.getFile());
1147        }
1148        return null;
1149    }
1150
1151    /*
1152     * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getValidateStateRule(java.lang.Object)
1153     * @since 3.0
1154     */

1155    protected ISchedulingRule getValidateStateRule(Object JavaDoc element) {
1156        if (element instanceof IFileEditorInput) {
1157            IFileEditorInput input= (IFileEditorInput) element;
1158            return fResourceRuleFactory.validateEditRule(new IResource[] { input.getFile() });
1159        }
1160        return null;
1161    }
1162
1163    /**
1164     * Returns whether the underlying file has a BOM.
1165     *
1166     * @param element the element, or <code>null</code>
1167     * @return <code>true</code> if the underlying file has BOM
1168     */

1169    private boolean hasBOM(Object JavaDoc element) {
1170        if (element instanceof IFileEditorInput) {
1171            IFile file= ((IFileEditorInput)element).getFile();
1172            if (file != null) {
1173                try {
1174                    IContentDescription description= file.getContentDescription();
1175                    return description != null && description.getProperty(IContentDescription.BYTE_ORDER_MARK) != null;
1176                } catch (CoreException ex) {
1177                    return false;
1178                }
1179            }
1180        }
1181        return false;
1182    }
1183
1184    /**
1185     * Reads the file's UTF-8 BOM if any and stores it.
1186     * <p>
1187     * XXX:
1188     * This is a workaround for a corresponding bug in Java readers and writer,
1189     * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
1190     * </p>
1191     * @param file the file
1192     * @param encoding the encoding
1193     * @param element the element, or <code>null</code>
1194     * @throws org.eclipse.core.runtime.CoreException if reading the BOM fails
1195     * @since 3.0
1196     * @deprecated as of 3.0 this method is no longer in use and does nothing
1197     */

1198    protected void readUTF8BOM(IFile file, String JavaDoc encoding, Object JavaDoc element) throws CoreException {
1199    }
1200
1201    /**
1202     * Internally caches the file's encoding data.
1203     *
1204     * @param element the element, or <code>null</code>
1205     * @throws CoreException if the encoding cannot be retrieved
1206     * @since 3.1
1207     */

1208    protected void cacheEncodingState(Object JavaDoc element) throws CoreException {
1209        if (element instanceof IFileEditorInput) {
1210            IFileEditorInput editorInput= (IFileEditorInput)element;
1211            IFile file= editorInput.getFile();
1212            if (file != null) {
1213                ElementInfo info= getElementInfo(element);
1214                if (info instanceof StorageInfo)
1215                    ((StorageInfo)info).fEncoding= getPersistedEncoding(element);
1216
1217                if (info instanceof FileInfo)
1218                    ((FileInfo)info).fHasBOM= hasBOM(element);
1219            }
1220        }
1221    }
1222    
1223    /**
1224     * Computes the scheduling rule needed to create or modify a resource. If
1225     * the resource exists, its modify rule is returned. If it does not, the
1226     * resource hierarchy is iterated towards the workspace root to find the
1227     * first parent of <code>toCreateOrModify</code> that exists. Then the
1228     * 'create' rule for the last non-existing resource is returned.
1229     *
1230     * @param toCreateOrModify the resource to create or modify
1231     * @return the minimal scheduling rule needed to modify or create a resource
1232     */

1233    private ISchedulingRule computeSchedulingRule(IResource toCreateOrModify) {
1234        if (toCreateOrModify.exists())
1235            return fResourceRuleFactory.modifyRule(toCreateOrModify);
1236
1237        IResource parent= toCreateOrModify;
1238        do {
1239            toCreateOrModify= parent;
1240            parent= toCreateOrModify.getParent();
1241        } while (parent != null && !parent.exists());
1242
1243        return fResourceRuleFactory.createRule(toCreateOrModify);
1244    }
1245}
1246
Popular Tags