blob: d28d10ba04e9d7360a745db7e2559c0cc1ac022c [file] [log] [blame]
/**********************************************************************
Copyright (c) 2000, 2003 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.core.internal.filebuffers;
import java.io.File;
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.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.filebuffers.FileBuffers;
public abstract class ResourceFileBuffer extends AbstractFileBuffer {
/**
* 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.
*/
private class SafeFileChange implements Runnable {
/**
* Creates a new safe runnable for the given file.
*/
public SafeFileChange() {
}
/**
* Execute the change.
* Subclass responsibility.
*
* @exception an exception in case of error
*/
protected void execute() throws Exception {
}
/**
* Does everything necessary prior to execution.
*/
public void preRun() {
fManager.fireStateChanging(ResourceFileBuffer.this);
}
/*
* @see java.lang.Runnable#run()
*/
public void run() {
if (isDisposed()) {
fManager.fireStateChangeFailed(ResourceFileBuffer.this);
return;
}
try {
execute();
} catch (Exception x) {
FileBuffersPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, "Exception when synchronizing", x)); //$NON-NLS-1$
fManager.fireStateChangeFailed(ResourceFileBuffer.this);
}
}
}
/**
* Synchronizes the document with external resource changes.
*/
private class FileSynchronizer implements IResourceChangeListener, IResourceDeltaVisitor {
/** A flag indicating whether this synchronizer is installed or not. */
private boolean fIsInstalled= false;
/**
* Creates a new file synchronizer. Is not yet installed on a file.
*/
public FileSynchronizer() {
}
/**
* Installs the synchronizer on the file.
*/
public void install() {
fFile.getWorkspace().addResourceChangeListener(this);
fIsInstalled= true;
}
/**
* Uninstalls the synchronizer from the file.
*/
public void uninstall() {
fFile.getWorkspace().removeResourceChangeListener(this);
fIsInstalled= false;
}
/*
* @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent e) {
IResourceDelta delta= e.getDelta();
try {
if (delta != null && fIsInstalled)
delta.accept(this);
} catch (CoreException x) {
handleCoreException(x);
}
}
/*
* @see IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
*/
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta != null && fFile.equals(delta.getResource())) {
SafeFileChange fileChange= null;
switch (delta.getKind()) {
case IResourceDelta.CHANGED:
if ((IResourceDelta.CONTENT & delta.getFlags()) != 0) {
if (!isDisposed() && !fCanBeSaved && !isSynchronized()) {
fileChange= new SafeFileChange() {
protected void execute() throws Exception {
handleFileContentChanged();
}
};
}
}
break;
case IResourceDelta.REMOVED:
if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) {
final IPath path= delta.getMovedToPath();
fileChange= new SafeFileChange() {
protected void execute() throws Exception {
handleFileMoved(path);
}
};
} else {
if (!isDisposed() && !fCanBeSaved) {
fileChange= new SafeFileChange() {
protected void execute() throws Exception {
handleFileDeleted();
}
};
}
}
break;
}
if (fileChange != null) {
fileChange.preRun();
fManager.execute(fileChange, fSynchronizationContextCount > 0);
}
}
return true; // because we are sitting on files anyway
}
}
/** The location */
protected IPath fLocation;
/** The element for which the info is stored */
protected IFile fFile;
/** How often the element has been connected */
protected int fReferenceCount;
/** Can the element be saved */
protected boolean fCanBeSaved= false;
/** Has element state been validated */
protected boolean fIsStateValidated= false;
/** The status of this element */
protected IStatus fStatus;
/** The file synchronizer. */
protected FileSynchronizer fFileSynchronizer;
/** The time stamp at which this buffer synchronized with the underlying file. */
protected long fSynchronizationStamp= IFile.NULL_STAMP;
/** How often the synchronization context has been requested */
protected int fSynchronizationContextCount;
/** The text file buffer manager */
protected TextFileBufferManager fManager;
public ResourceFileBuffer(TextFileBufferManager manager) {
super();
fManager= manager;
}
abstract protected void handleFileContentChanged();
abstract protected void addFileBufferContentListeners();
abstract protected void removeFileBufferContentListeners();
abstract protected void initializeFileBufferContent(IProgressMonitor monitor) throws CoreException;
abstract protected void commitFileBufferContent(IProgressMonitor monitor, boolean overwrite) throws CoreException;
public void create(IPath location, IProgressMonitor monitor) throws CoreException {
IFile file= FileBuffers.getWorkspaceFileAtLocation(location);
if (file == null || !file.exists())
throw new CoreException(new Status(IStatus.ERROR, FileBuffersPlugin.PLUGIN_ID, IStatus.OK, "File does not exist", null));
fLocation= location;
fFile= file;
fFileSynchronizer= new FileSynchronizer();
refreshFile(monitor);
initializeFileBufferContent(monitor);
fSynchronizationStamp= fFile.getModificationStamp();
addFileBufferContentListeners();
}
public void connect() {
++ fReferenceCount;
if (fReferenceCount == 1)
fFileSynchronizer.install();
}
public void disconnect() throws CoreException {
-- fReferenceCount;
if (fReferenceCount == 0) {
if (fFileSynchronizer != null)
fFileSynchronizer.uninstall();
fFileSynchronizer= null;
}
}
/**
* Returns whether this file buffer has already been disposed.
*
* @return <code>true</code> if already disposed, <code>false</code> otherwise
*/
public boolean isDisposed() {
return fFileSynchronizer == null;
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#getLocation()
*/
public IPath getLocation() {
return fLocation;
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#commit(org.eclipse.core.runtime.IProgressMonitor, boolean)
*/
public void commit(IProgressMonitor monitor, boolean overwrite) throws CoreException {
if (!isDisposed() && fCanBeSaved) {
fManager.fireStateChanging(this);
try {
commitFileBufferContent(monitor, overwrite);
} catch (CoreException x) {
fManager.fireStateChangeFailed(this);
throw x;
} catch (RuntimeException x) {
fManager.fireStateChangeFailed(this);
throw x;
}
fCanBeSaved= false;
addFileBufferContentListeners();
fManager.fireDirtyStateChanged(this, fCanBeSaved);
}
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#isDirty()
*/
public boolean isDirty() {
return fCanBeSaved;
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#isShared()
*/
public boolean isShared() {
return fReferenceCount > 1;
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#validateState(org.eclipse.core.runtime.IProgressMonitor, java.lang.Object)
*/
public void validateState(IProgressMonitor monitor, Object computationContext) throws CoreException {
if (!isDisposed() && !fIsStateValidated) {
if (fFile.isReadOnly()) {
IWorkspace workspace= fFile.getWorkspace();
fStatus= workspace.validateEdit(new IFile[] { fFile }, computationContext);
}
fIsStateValidated= true;
fManager.fireStateValidationChanged(this, fIsStateValidated);
}
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#isStateValidated()
*/
public boolean isStateValidated() {
return fIsStateValidated;
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#resetStateValidation()
*/
public void resetStateValidation() {
if (fIsStateValidated) {
fIsStateValidated= false;
fManager.fireStateValidationChanged(this, fIsStateValidated);
}
}
/**
* Sends out the notification that the file serving as document input has been moved.
*
* @param newLocation the path of the new location of the file
*/
protected void handleFileMoved(IPath newLocation) {
fManager.fireUnderlyingFileMoved(this, newLocation);
}
/**
* Sends out the notification that the file serving as document input has been deleted.
*/
protected void handleFileDeleted() {
fManager.fireUnderlyingFileDeleted(this);
}
/**
* Refreshes the given file.
*/
protected void refreshFile(IProgressMonitor monitor) {
try {
fFile.refreshLocal(IFile.DEPTH_INFINITE, monitor);
} catch (OperationCanceledException x) {
} catch (CoreException x) {
handleCoreException(x);
}
}
/**
* Defines the standard procedure to handle <code>CoreExceptions</code>. Exceptions
* are written to the plug-in log.
*
* @param exception the exception to be logged
* @param message the message to be logged
*/
protected void handleCoreException(CoreException exception) {
ILog log= Platform.getPlugin(FileBuffersPlugin.PLUGIN_ID).getLog();
log.log(exception.getStatus());
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#isSynchronized()
*/
public boolean isSynchronized() {
return fSynchronizationStamp == fFile.getModificationStamp() && fFile.isSynchronized(IResource.DEPTH_ZERO);
}
/*
* @see org.eclipse.core.filebuffers.IFileBuffer#getModificationStamp()
*/
public long getModificationStamp() {
File file= FileBuffers.getSystemFileAtLocation(getLocation());
if (file != null)
return file.lastModified();
return IResource.NULL_STAMP;
}
/**
* Requests the file buffer manager's synchronization context for this file buffer.
*/
public void requestSynchronizationContext() {
++ fSynchronizationContextCount;
}
/**
* Releases the file buffer manager's synchronization context for this file buffer.
*/
public void releaseSynchronizationContext() {
-- fSynchronizationContextCount;
}
}