package org.eclipse.ltk.core.refactoring;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
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.core.resources.IFile;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.UndoEdit;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.ltk.internal.core.refactoring.BufferValidationState;
import org.eclipse.ltk.internal.core.refactoring.Changes;
import org.eclipse.ltk.internal.core.refactoring.ContentStamps;
import org.eclipse.ltk.internal.core.refactoring.Lock;
import org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin;
public class UndoTextFileChange extends Change {
private String fName;
private UndoEdit fUndo;
private IFile fFile;
private ContentStamp fContentStampToRestore;
private int fSaveMode;
private boolean fDirty;
private BufferValidationState fValidationState;
protected UndoTextFileChange(String name, IFile file, UndoEdit undo, ContentStamp stamp, int saveMode) {
Assert.isNotNull(name);
Assert.isNotNull(file);
Assert.isNotNull(undo);
fName= name;
fFile= file;
fUndo= undo;
fContentStampToRestore= stamp;
fSaveMode= saveMode;
}
public int getSaveMode() {
return fSaveMode;
}
@Override
public String getName() {
return fName;
}
protected Change createUndoChange(UndoEdit edit, ContentStamp stampToRestore) throws CoreException {
return new UndoTextFileChange(getName(), fFile, edit, stampToRestore, fSaveMode);
}
@Override
public Object getModifiedElement() {
return fFile;
}
@Override
public Object[] getAffectedObjects() {
Object modifiedElement= getModifiedElement();
if (modifiedElement == null)
return null;
return new Object[] { modifiedElement };
}
@Override
public void initializeValidationData(IProgressMonitor pm) {
if (pm == null)
pm= new NullProgressMonitor();
pm.beginTask("", 1);
try {
fValidationState= BufferValidationState.create(fFile);
} finally {
pm.done();
}
}
@Override
public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException {
if (pm == null)
pm= new NullProgressMonitor();
pm.beginTask("", 1);
try {
if (fValidationState == null)
throw new CoreException(new Status(IStatus.ERROR, RefactoringCorePlugin.getPluginId(), "UndoTextFileChange has not been initialialized"));
ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(fFile.getFullPath(), LocationKind.IFILE);
fDirty= buffer != null && buffer.isDirty();
return fValidationState.isValid(needsSaving(), true);
} finally {
pm.done();
}
}
@Override
public Change perform(IProgressMonitor pm) throws CoreException {
if (pm == null)
pm= new NullProgressMonitor();
ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
pm.beginTask("", 2);
ITextFileBuffer buffer= null;
try {
manager.connect(fFile.getFullPath(), LocationKind.IFILE, new SubProgressMonitor(pm, 1));
buffer= manager.getTextFileBuffer(fFile.getFullPath(), LocationKind.IFILE);
IDocument document= buffer.getDocument();
ContentStamp currentStamp= ContentStamps.get(fFile, document);
boolean[] setContentStampSuccess= { false };
UndoEdit redo= performEdits(buffer, document, setContentStampSuccess);
if (needsSaving()) {
buffer.commit(pm, false);
if (!setContentStampSuccess[0]) {
ContentStamps.set(fFile, fContentStampToRestore);
}
}
return createUndoChange(redo, currentStamp);
} catch (BadLocationException e) {
if (fValidationState == null || !fValidationState.wasDerived())
throw Changes.asCoreException(e);
else
return new NullChange();
} catch (MalformedTreeException e) {
if (fValidationState == null || !fValidationState.wasDerived())
throw Changes.asCoreException(e);
else
return new NullChange();
} catch (CoreException e) {
if (fValidationState == null || !fValidationState.wasDerived())
throw e;
else
return new NullChange();
} finally {
if (buffer != null)
manager.disconnect(fFile.getFullPath(), LocationKind.IFILE, new SubProgressMonitor(pm, 1));
}
}
private UndoEdit performEdits(ITextFileBuffer buffer, final IDocument document, final boolean[] setContentStampSuccess) throws MalformedTreeException, BadLocationException, CoreException {
if (! buffer.isSynchronizationContextRequested()) {
return doPerformEdits(document, setContentStampSuccess);
}
ITextFileBufferManager fileBufferManager= FileBuffers.getTextFileBufferManager();
final Lock completionLock= new Lock();
final UndoEdit[] result= new UndoEdit[1];
final BadLocationException[] badLocationException= new BadLocationException[1];
final MalformedTreeException[] malformedTreeException= new MalformedTreeException[1];
final CoreException[] coreException= new CoreException[1];
Runnable runnable= new Runnable() {
@Override
public void run() {
synchronized (completionLock) {
try {
result[0]= doPerformEdits(document, setContentStampSuccess);
} catch (BadLocationException e) {
badLocationException[0]= e;
} catch (MalformedTreeException e) {
malformedTreeException[0]= e;
} catch (CoreException e) {
coreException[0]= e;
} finally {
completionLock.fDone= true;
completionLock.notifyAll();
}
}
}
};
synchronized (completionLock) {
fileBufferManager.execute(runnable);
while (! completionLock.fDone) {
try {
completionLock.wait(500);
} catch (InterruptedException x) {
}
}
}
if (badLocationException[0] != null) {
throw badLocationException[0];
} else if (malformedTreeException[0] != null) {
throw malformedTreeException[0];
} else if (coreException[0] != null) {
throw coreException[0];
}
return result[0];
}
private UndoEdit doPerformEdits(IDocument document, boolean[] setContentStampSuccess) throws MalformedTreeException, BadLocationException, CoreException {
LinkedModeModel.closeAllModels(document);
UndoEdit redo= fUndo.apply(document, TextEdit.CREATE_UNDO);
setContentStampSuccess[0]= ContentStamps.set(document, fContentStampToRestore);
return redo;
}
@Override
public void dispose() {
if (fValidationState != null) {
fValidationState.dispose();
}
}
private boolean needsSaving() {
return (fSaveMode & TextFileChange.FORCE_SAVE) != 0 || !fDirty && (fSaveMode & TextFileChange.KEEP_SAVE_STATE) != 0;
}
}