Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java')
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java670
1 files changed, 670 insertions, 0 deletions
diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java b/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java
new file mode 100644
index 00000000000..70f7e6a8d1b
--- /dev/null
+++ b/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java
@@ -0,0 +1,670 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.ui.editors.text;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.source.IAnnotationModel;
+
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ContainerGenerator;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.texteditor.IElementStateListener;
+import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
+
+
+
+/**
+ * Shareable document provider specialized for file resources (<code>IFile</code>).<p>
+ * This class may be instantiated or be subclassed.
+ */
+public class FileDocumentProvider extends StorageDocumentProvider {
+
+
+ /**
+ * Runnable encapsulating an element state change. This runnable ensures
+ * that a element change failed message is sent out to the element state listeners
+ * in case an exception occurred.
+ * @since 2.0
+ */
+ protected class SafeChange implements Runnable {
+
+ /** The input that changes. */
+ private IFileEditorInput fInput;
+
+ /**
+ * Creates a new safe runnable for the given input.
+ * @param input the input
+ */
+ public SafeChange(IFileEditorInput input) {
+ fInput= input;
+ }
+
+ /**
+ * Subclass responsibility.
+ * @param input the input
+ * @exception Exception in case of error
+ */
+ protected void execute(IFileEditorInput input) throws Exception {
+ }
+
+ /*
+ * @see java.lang.Runnable#run()
+ * @since 2.0
+ */
+ public void run() {
+
+ if (getElementInfo(fInput) == null) {
+ fireElementStateChangeFailed(fInput);
+ return;
+ }
+
+ try {
+ execute(fInput);
+ } catch (Exception e) {
+ fireElementStateChangeFailed(fInput);
+ }
+ }
+ };
+
+
+ /**
+ * Synchronizes the document with external resource changes.
+ */
+ protected class FileSynchronizer implements IResourceChangeListener, IResourceDeltaVisitor {
+
+ /** The file editor input */
+ protected IFileEditorInput fFileEditorInput;
+
+ /**
+ * Creates a new file synchronizer. Is not yet installed on a resource.
+ * @param fileEditorInput the editor input to be synchronized
+ */
+ public FileSynchronizer(IFileEditorInput fileEditorInput) {
+ fFileEditorInput= fileEditorInput;
+ };
+
+ /**
+ * Creates a new file synchronizer. Is not yet installed on a resource.
+ * @param fileEditorInput the editor input to be synchronized
+ * @deprecated use FileSynchronizer(IFileEditorInput)
+ */
+ public FileSynchronizer(FileEditorInput fileEditorInput) {
+ fFileEditorInput= fileEditorInput;
+ };
+
+ /**
+ * Returns the file wrapped by the file editor input.
+ * @return the file wrapped by the editor input associated with that synchronizer
+ */
+ protected IFile getFile() {
+ return fFileEditorInput.getFile();
+ }
+
+ /**
+ * Installs the synchronizer on the input's file.
+ */
+ public void install() {
+ getFile().getWorkspace().addResourceChangeListener(this);
+ }
+
+ /**
+ * Uninstalls the synchronizer from the input's file.
+ */
+ public void uninstall() {
+ getFile().getWorkspace().removeResourceChangeListener(this);
+ }
+
+ /*
+ * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
+ */
+ public void resourceChanged(IResourceChangeEvent e) {
+ IResourceDelta delta= e.getDelta();
+ try {
+ if (delta != null)
+ delta.accept(this);
+ } catch (CoreException x) {
+ handleCoreException(x, TextEditorMessages.getString("FileDocumentProvider.resourceChanged")); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * @see IResourceDeltaVisitor#visit(IResourceDelta)
+ */
+ public boolean visit(IResourceDelta delta) throws CoreException {
+
+ if (delta != null && getFile().equals(delta.getResource())) {
+
+ Runnable runnable= null;
+
+ switch (delta.getKind()) {
+ case IResourceDelta.CHANGED:
+ if ((IResourceDelta.CONTENT & delta.getFlags()) != 0) {
+ FileInfo info= (FileInfo) getElementInfo(fFileEditorInput);
+ if (!info.fCanBeSaved && computeModificationStamp(getFile()) != info.fModificationStamp) {
+ runnable= new SafeChange(fFileEditorInput) {
+ protected void execute(IFileEditorInput input) throws Exception {
+ handleElementContentChanged(input);
+ }
+ };
+ }
+ }
+ break;
+ case IResourceDelta.REMOVED:
+ if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) {
+ final IPath path= delta.getMovedToPath();
+ runnable= new SafeChange(fFileEditorInput) {
+ protected void execute(IFileEditorInput input) throws Exception {
+ handleElementMoved(input, path);
+ }
+ };
+ } else {
+ FileInfo info= (FileInfo) getElementInfo(fFileEditorInput);
+ if (!info.fCanBeSaved) {
+ runnable= new SafeChange(fFileEditorInput) {
+ protected void execute(IFileEditorInput input) throws Exception {
+ handleElementDeleted(input);
+ }
+ };
+ }
+ }
+ break;
+ }
+
+ if (runnable != null)
+ update(runnable);
+ }
+
+ return true; // because we are sitting on files anyway
+ }
+
+ /**
+ * Posts the update code "behind" the running operation.
+ *
+ * @param runnable the update code
+ */
+ protected void update(Runnable runnable) {
+
+ if (runnable instanceof SafeChange)
+ fireElementStateChanging(fFileEditorInput);
+
+ IWorkbench workbench= PlatformUI.getWorkbench();
+ IWorkbenchWindow[] windows= workbench.getWorkbenchWindows();
+ if (windows != null && windows.length > 0) {
+ Display display= windows[0].getShell().getDisplay();
+ display.asyncExec(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+ };
+
+
+
+ /**
+ * Bundle of all required information to allow files as underlying document resources.
+ */
+ protected class FileInfo extends StorageInfo {
+
+ /** The file synchronizer */
+ public FileSynchronizer fFileSynchronizer;
+ /** The time stamp at which this provider changed the file */
+ public long fModificationStamp= IResource.NULL_STAMP;
+
+ /**
+ * Creates a new file info.
+ * @param document the document
+ * @param model the annotation model
+ * @param fileSynchronizer the file synchronizer
+ */
+ public FileInfo(IDocument document, IAnnotationModel model, FileSynchronizer fileSynchronizer) {
+ super(document, model);
+ fFileSynchronizer= fileSynchronizer;
+ }
+ };
+
+
+ /**
+ * Creates a new document provider.
+ */
+ public FileDocumentProvider() {
+ super();
+ }
+
+ /**
+ * Overrides <code>StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput)</code>.
+ * @deprecated use file encoding based version
+ * @since 2.0
+ */
+ protected boolean setDocumentContent(IDocument document, IEditorInput editorInput) throws CoreException {
+ if (editorInput instanceof IFileEditorInput) {
+ IFile file= ((IFileEditorInput) editorInput).getFile();
+ setDocumentContent(document, file.getContents(false));
+ return true;
+ }
+ return super.setDocumentContent(document, editorInput);
+ }
+
+ /*
+ * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput, String)
+ * @since 2.0
+ */
+ protected boolean setDocumentContent(IDocument document, IEditorInput editorInput, String encoding) throws CoreException {
+ if (editorInput instanceof IFileEditorInput) {
+ IFile file= ((IFileEditorInput) editorInput).getFile();
+ setDocumentContent(document, file.getContents(false), encoding);
+ return true;
+ }
+ return super.setDocumentContent(document, editorInput, encoding);
+ }
+
+ /*
+ * @see AbstractDocumentProvider#createAnnotationModel(Object)
+ */
+ protected IAnnotationModel createAnnotationModel(Object element) throws CoreException {
+ if (element instanceof IFileEditorInput) {
+ IFileEditorInput input= (IFileEditorInput) element;
+ return new ResourceMarkerAnnotationModel(input.getFile());
+ }
+
+ return super.createAnnotationModel(element);
+ }
+
+ /**
+ * Checks whether the given resource has been changed on the
+ * local file system by comparing the actual time stamp with the
+ * cached one. If the resource has been changed, a <code>CoreException</code>
+ * is thrown.
+ *
+ * @param cachedModificationStamp the chached modification stamp
+ * @param resource the resource to check
+ * @exception CoreException if resource has been changed on the file system
+ */
+ protected void checkSynchronizationState(long cachedModificationStamp, IResource resource) throws CoreException {
+ if (cachedModificationStamp != computeModificationStamp(resource)) {
+ Status status= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IResourceStatus.OUT_OF_SYNC_LOCAL, TextEditorMessages.getString("FileDocumentProvider.error.out_of_sync"), null); //$NON-NLS-1$
+ throw new CoreException(status);
+ }
+ }
+
+ /**
+ * Computes the initial modification stamp for the given resource.
+ *
+ * @param resource the resource
+ * @return the modification stamp
+ */
+ protected long computeModificationStamp(IResource resource) {
+ long modificationStamp= resource.getModificationStamp();
+
+ IPath path= resource.getLocation();
+ if (path == null)
+ return modificationStamp;
+
+ modificationStamp= path.toFile().lastModified();
+ return modificationStamp;
+ }
+
+ /*
+ * @see IDocumentProvider#getModificationStamp(Object)
+ */
+ public long getModificationStamp(Object element) {
+
+ if (element instanceof IFileEditorInput) {
+ IFileEditorInput input= (IFileEditorInput) element;
+ return computeModificationStamp(input.getFile());
+ }
+
+ return super.getModificationStamp(element);
+ }
+
+ /*
+ * @see IDocumentProvider#getSynchronizationStamp(Object)
+ */
+ public long getSynchronizationStamp(Object element) {
+
+ if (element instanceof IFileEditorInput) {
+ FileInfo info= (FileInfo) getElementInfo(element);
+ return info.fModificationStamp;
+ }
+
+ return super.getSynchronizationStamp(element);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.IDocumentProviderExtension#synchronize(Object)
+ * @since 2.0
+ */
+ public void synchronize(Object element) throws CoreException {
+ if (element instanceof IFileEditorInput) {
+
+ IFileEditorInput input= (IFileEditorInput) element;
+
+ FileInfo info= (FileInfo) getElementInfo(element);
+ if (info != null) {
+
+ info.fFileSynchronizer.uninstall();
+ input.getFile().refreshLocal(IResource.DEPTH_INFINITE, null);
+ info.fFileSynchronizer.install();
+
+ handleElementContentChanged((IFileEditorInput) element);
+ }
+ return;
+
+ }
+ super.synchronize(element);
+ }
+
+ /*
+ * @see IDocumentProvider#isDeleted(Object)
+ */
+ public boolean isDeleted(Object element) {
+
+ if (element instanceof IFileEditorInput) {
+ IFileEditorInput input= (IFileEditorInput) element;
+
+ IPath path= input.getFile().getLocation();
+ if (path == null)
+ return true;
+
+ return !path.toFile().exists();
+ }
+
+ return super.isDeleted(element);
+ }
+
+ /*
+ * @see AbstractDocumentProvider#doSaveDocument(IProgressMonitor, Object, IDocument, boolean)
+ */
+ protected void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException {
+ if (element instanceof IFileEditorInput) {
+
+ IFileEditorInput input= (IFileEditorInput) element;
+
+ try {
+
+ InputStream stream= new ByteArrayInputStream(document.get().getBytes(ResourcesPlugin.getEncoding()));
+ IFile file= input.getFile();
+
+ if (file.exists()) {
+
+ FileInfo info= (FileInfo) getElementInfo(element);
+
+ if (info != null && !overwrite)
+ checkSynchronizationState(info.fModificationStamp, file);
+
+ // inform about the upcoming content change
+ fireElementStateChanging(element);
+ try {
+ file.setContents(stream, overwrite, true, monitor);
+ } catch (CoreException x) {
+ // inform about failure
+ fireElementStateChangeFailed(element);
+ throw x;
+ } catch (RuntimeException x) {
+ // inform about failure
+ fireElementStateChangeFailed(element);
+ throw x;
+ }
+
+ // If here, the editor state will be flipped to "not dirty".
+ // Thus, the state changing flag will be reset.
+
+ if (info != null) {
+
+ ResourceMarkerAnnotationModel model= (ResourceMarkerAnnotationModel) info.fModel;
+ model.updateMarkers(info.fDocument);
+
+ info.fModificationStamp= computeModificationStamp(file);
+ }
+
+ } else {
+ try {
+ monitor.beginTask(TextEditorMessages.getString("FileDocumentProvider.task.saving"), 2000); //$NON-NLS-1$
+ ContainerGenerator generator = new ContainerGenerator(file.getParent().getFullPath());
+ generator.generateContainer(new SubProgressMonitor(monitor, 1000));
+ file.create(stream, false, new SubProgressMonitor(monitor, 1000));
+ }
+ finally {
+ monitor.done();
+ }
+ }
+
+ } catch (IOException x) {
+ IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, x.getMessage(), x);
+ throw new CoreException(s);
+ }
+
+ } else {
+ super.doSaveDocument(monitor, element, document, overwrite);
+ }
+ }
+
+ /*
+ * @see AbstractDocumentProvider#createElementInfo(Object)
+ */
+ protected ElementInfo createElementInfo(Object element) throws CoreException {
+ if (element instanceof IFileEditorInput) {
+
+ IFileEditorInput input= (IFileEditorInput) element;
+
+ try {
+ input.getFile().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException x) {
+ handleCoreException(x,TextEditorMessages.getString("FileDocumentProvider.createElementInfo")); //$NON-NLS-1$
+ }
+
+ IDocument d= null;
+ IStatus s= null;
+
+ try {
+ d= createDocument(element);
+ } catch (CoreException x) {
+ s= x.getStatus();
+ d= new Document();
+ }
+
+ IAnnotationModel m= createAnnotationModel(element);
+ FileSynchronizer f= new FileSynchronizer(input);
+ f.install();
+
+ FileInfo info= new FileInfo(d, m, f);
+ info.fModificationStamp= computeModificationStamp(input.getFile());
+ info.fStatus= s;
+
+ return info;
+ }
+
+ return super.createElementInfo(element);
+ }
+
+ /*
+ * @see AbstractDocumentProvider#disposeElementInfo(Object, ElementInfo)
+ */
+ protected void disposeElementInfo(Object element, ElementInfo info) {
+ if (info instanceof FileInfo) {
+ FileInfo fileInfo= (FileInfo) info;
+ if (fileInfo.fFileSynchronizer != null)
+ fileInfo.fFileSynchronizer.uninstall();
+ }
+
+ super.disposeElementInfo(element, info);
+ }
+
+ /**
+ * Updates the element info to a change of the file content and sends out
+ * appropriate notifications.
+ *
+ * @param fileEditorInput the input of an text editor
+ */
+ protected void handleElementContentChanged(IFileEditorInput fileEditorInput) {
+ FileInfo info= (FileInfo) getElementInfo(fileEditorInput);
+
+ IDocument document= new Document();
+ IStatus status= null;
+
+ try {
+
+ try {
+ fileEditorInput.getFile().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException x) {
+ handleCoreException(x, "FileDocumentProvider.handleElementContentChanged"); //$NON-NLS-1$
+ }
+
+ setDocumentContent(document, fileEditorInput, info.fEncoding);
+
+ } catch (CoreException x) {
+ status= x.getStatus();
+ }
+
+ String newContent= document.get();
+
+ if ( !newContent.equals(info.fDocument.get())) {
+
+ // set the new content and fire content related events
+ fireElementContentAboutToBeReplaced(fileEditorInput);
+
+ removeUnchangedElementListeners(fileEditorInput, info);
+
+ info.fDocument.removeDocumentListener(info);
+ info.fDocument.set(newContent);
+ info.fCanBeSaved= false;
+ info.fModificationStamp= computeModificationStamp(fileEditorInput.getFile());
+ info.fStatus= status;
+
+ addUnchangedElementListeners(fileEditorInput, info);
+
+ fireElementContentReplaced(fileEditorInput);
+
+ } else {
+
+ removeUnchangedElementListeners(fileEditorInput, info);
+
+ // fires only the dirty state related event
+ info.fCanBeSaved= false;
+ info.fModificationStamp= computeModificationStamp(fileEditorInput.getFile());
+ info.fStatus= status;
+
+ addUnchangedElementListeners(fileEditorInput, info);
+
+ fireElementDirtyStateChanged(fileEditorInput, false);
+ }
+ }
+
+ /**
+ * Sends out the notification that the file serving as document input has been moved.
+ *
+ * @param fileEditorInput the input of an text editor
+ * @param path the path of the new location of the file
+ */
+ protected void handleElementMoved(IFileEditorInput fileEditorInput, IPath path) {
+ IWorkspace workspace= ResourcesPlugin.getWorkspace();
+ IFile newFile= workspace.getRoot().getFile(path);
+ fireElementMoved(fileEditorInput, newFile == null ? null : new FileEditorInput(newFile));
+ }
+
+ /**
+ * Sends out the notification that the file serving as document input has been deleted.
+ *
+ * @param fileEditorInput the input of an text editor
+ */
+ protected void handleElementDeleted(IFileEditorInput fileEditorInput) {
+ fireElementDeleted(fileEditorInput);
+ }
+
+ /*
+ * @see AbstractDocumentProvider#getElementInfo(Object)
+ * It's only here to circumvent visibility issues with certain compilers.
+ */
+ protected ElementInfo getElementInfo(Object element) {
+ return super.getElementInfo(element);
+ }
+
+ /*
+ * @see AbstractDocumentProvider#doValidateState(Object, Object)
+ * @since 2.0
+ */
+ protected void doValidateState(Object element, Object computationContext) throws CoreException {
+
+ if (element instanceof IFileEditorInput) {
+ IFileEditorInput input= (IFileEditorInput) element;
+ FileInfo info= (FileInfo) getElementInfo(input);
+ if (info != null) {
+ IFile file= input.getFile();
+ if (file.isReadOnly()) { // do not use cached state here
+ IWorkspace workspace= file.getWorkspace();
+ workspace.validateEdit(new IFile[] { file }, computationContext);
+ }
+ }
+ }
+
+ super.doValidateState(element, computationContext);
+ }
+
+ /*
+ * @see IDocumentProviderExtension#isModifiable(Object)
+ * @since 2.0
+ */
+ public boolean isModifiable(Object element) {
+ if (!isStateValidated(element)) {
+ if (element instanceof IFileEditorInput)
+ return true;
+ }
+ return super.isModifiable(element);
+ }
+
+ /*
+ * @see IDocumentProvider#resetDocument(Object)
+ * @since 2.0
+ */
+ public void resetDocument(Object element) throws CoreException {
+ // http://dev.eclipse.org/bugs/show_bug.cgi?id=19014
+ if (element instanceof IFileEditorInput) {
+ IFileEditorInput input= (IFileEditorInput) element;
+ try {
+ input.getFile().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException x) {
+ handleCoreException(x,TextEditorMessages.getString("FileDocumentProvider.resetDocument")); //$NON-NLS-1$
+ }
+ }
+ super.resetDocument(element);
+ }
+} \ No newline at end of file

Back to the top