Copyright (c) 2000, 2013 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.ltk.internal.core.refactoring; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.IFileBuffer; import org.eclipse.core.filebuffers.IFileBufferListener; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.ITextFileBufferManager; import org.eclipse.core.filebuffers.LocationKind; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.ltk.core.refactoring.RefactoringStatus; public abstract class BufferValidationState { protected final IFile fFile; protected final boolean fExisted; protected final boolean fDerived; protected final boolean fWasDirty; protected final String fEncoding; protected static class ModificationStamp { private int fKind; private long fValue; public static final int FILE= 1; public static final int DOCUMENT= 2; public static ModificationStamp createFile(long value) { return new ModificationStamp(FILE, value); } public static ModificationStamp createDocument(long value) { return new ModificationStamp(DOCUMENT, value); } private ModificationStamp(int kind, long value) { fKind= kind; fValue= value; } public boolean isFileStamp() { return fKind == FILE; } public boolean isDocumentStamp() { return fKind == DOCUMENT; } public int getKind() { return fKind; } public long getValue() { return fValue; } } public static BufferValidationState create(IFile file) { ITextFileBuffer buffer= getBuffer(file); if (buffer == null) { return new ModificationStampValidationState(file); } else { IDocument document= buffer.getDocument(); if (document instanceof IDocumentExtension4) { return new ModificationStampValidationState(file); } else { if (buffer.isDirty()) { return new NoStampValidationState(file); } else { return new ModificationStampValidationState(file); } } } } public boolean wasDirty() { return fWasDirty; } public boolean wasDerived() { return fDerived; } public RefactoringStatus isValid(boolean needsSaving) throws CoreException { return isValid(needsSaving, false); } public RefactoringStatus isValid(boolean needsSaving, boolean resilientForDerived) throws CoreException { if (resilientForDerived && fDerived) { return new RefactoringStatus(); } if (!fExisted) { if (fFile.exists()) return RefactoringStatus.createFatalErrorStatus(Messages.format( RefactoringCoreMessages.TextChanges_error_existing, BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); } else { if (!fFile.exists()) return RefactoringStatus.createFatalErrorStatus(Messages.format( RefactoringCoreMessages.TextChanges_error_not_existing, BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); } if (needsSaving) { if (fFile.isReadOnly()) { return RefactoringStatus.createFatalErrorStatus(Messages.format( RefactoringCoreMessages.TextChanges_error_read_only, BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); } else if (!fFile.isSynchronized(IResource.DEPTH_ZERO)) { return RefactoringStatus.createFatalErrorStatus(Messages.format( RefactoringCoreMessages.TextChanges_error_outOfSync, BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); } } if (fEncoding == null) { return RefactoringStatus.createFatalErrorStatus(Messages.format( RefactoringCoreMessages.BufferValidationState_no_character_encoding, BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); } else if (!fEncoding.equals(fFile.getCharset(true))) { return RefactoringStatus.createFatalErrorStatus(Messages.format( RefactoringCoreMessages.BufferValidationState_character_encoding_changed, BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); } return new RefactoringStatus(); } public void dispose() { } protected BufferValidationState(IFile file) { fFile= file; fExisted= file.exists(); fDerived= file.isDerived(); fWasDirty= isDirty(fFile); String encoding; try { encoding= file.getCharset(true); } catch (CoreException e) { encoding= null; } fEncoding= encoding; } protected IDocument getDocument() { ITextFileBuffer buffer= getBuffer(fFile); if (buffer == null) return null; return buffer.getDocument(); } protected static boolean isDirty(IFile file) { ITextFileBuffer buffer= getBuffer(file); if (buffer == null) return false; return buffer.isDirty(); } protected static ITextFileBuffer getBuffer(IFile file) { ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); IPath path= file.getFullPath(); ITextFileBuffer buffer= manager.getTextFileBuffer(path, LocationKind.IFILE); return buffer; } protected ModificationStamp getModificationStamp() { ITextFileBuffer buffer= getBuffer(fFile); if (buffer == null) { return ModificationStamp.createFile(fFile.getModificationStamp()); } else { IDocument document= buffer.getDocument(); if (document instanceof IDocumentExtension4) { return ModificationStamp.createDocument(((IDocumentExtension4)document).getModificationStamp()); } else { return ModificationStamp.createFile(fFile.getModificationStamp()); } } } }
Buffer validation state for dirty files whose document does not support modification stamps.
/** * Buffer validation state for dirty files whose document does not support * modification stamps. */
class NoStampValidationState extends BufferValidationState { private IDocumentListener fDocumentListener; private FileBufferListener fFileBufferListener; private boolean fChanged; private long fContentStamp= IResource.NULL_STAMP; class DocumentChangedListener implements IDocumentListener { @Override public void documentAboutToBeChanged(DocumentEvent event) { } @Override public void documentChanged(DocumentEvent event) { NoStampValidationState.this.documentChanged(); } } class FileBufferListener implements IFileBufferListener { @Override public void bufferCreated(IFileBuffer buffer) { // begin https://bugs.eclipse.org/bugs/show_bug.cgi?id=67821 if (buffer.getLocation().equals(fFile.getFullPath()) && buffer instanceof ITextFileBuffer) { ITextFileBuffer textBuffer= (ITextFileBuffer)buffer; if (fDocumentListener == null) fDocumentListener= new DocumentChangedListener(); textBuffer.getDocument().addDocumentListener(fDocumentListener); } // end fix https://bugs.eclipse.org/bugs/show_bug.cgi?id=67821 } @Override public void bufferDisposed(IFileBuffer buffer) { // begin fix https://bugs.eclipse.org/bugs/show_bug.cgi?id=67821 if (fDocumentListener != null && buffer.getLocation().equals(fFile.getFullPath())) { if (buffer instanceof ITextFileBuffer) { ITextFileBuffer textBuffer= (ITextFileBuffer)buffer; textBuffer.getDocument().removeDocumentListener(fDocumentListener); fDocumentListener= null; } fContentStamp= fFile.getModificationStamp(); } // end fix https://bugs.eclipse.org/bugs/show_bug.cgi?id=67821 } @Override public void bufferContentAboutToBeReplaced(IFileBuffer buffer) { } @Override public void bufferContentReplaced(IFileBuffer buffer) { } @Override public void stateChanging(IFileBuffer buffer) { } @Override public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) { } @Override public void stateValidationChanged(IFileBuffer buffer, boolean isStateValidated) { } @Override public void underlyingFileMoved(IFileBuffer buffer, IPath path) { } @Override public void underlyingFileDeleted(IFileBuffer buffer) { } @Override public void stateChangeFailed(IFileBuffer buffer) { } } public NoStampValidationState(IFile file) { super(file); fContentStamp= file.getModificationStamp(); fFileBufferListener= new FileBufferListener(); FileBuffers.getTextFileBufferManager().addFileBufferListener(fFileBufferListener); fDocumentListener= new DocumentChangedListener(); getDocument().addDocumentListener(fDocumentListener); } @Override public RefactoringStatus isValid(boolean needsSaving, boolean resilientForDerived) throws CoreException { RefactoringStatus result= super.isValid(needsSaving, resilientForDerived); if (result.hasFatalError()) return result; // If we have initialized the content stamp with the 'null' stamp then we can't compare it with // the current stamp since a change executed later could have set a concrete stamp for the // current content // if (fChanged || (fContentStamp != IResource.NULL_STAMP && fContentStamp != fFile.getModificationStamp()) if (fChanged || fContentStamp != fFile.getModificationStamp()) { result.addFatalError(Messages.format( RefactoringCoreMessages.TextChanges_error_content_changed, BasicElementLabels.getPathLabel(fFile.getFullPath(), false) )); } return result; } @Override public void dispose() { if (fFileBufferListener != null) { FileBuffers.getTextFileBufferManager().removeFileBufferListener(fFileBufferListener); // fix https://bugs.eclipse.org/bugs/show_bug.cgi?id=67821 fFileBufferListener= null; } if (fDocumentListener != null) { getDocument().removeDocumentListener(fDocumentListener); // fix https://bugs.eclipse.org/bugs/show_bug.cgi?id=67821 fDocumentListener= null; } } private void documentChanged() { fChanged= true; getDocument().removeDocumentListener(fDocumentListener); FileBuffers.getTextFileBufferManager().removeFileBufferListener(fFileBufferListener); fFileBufferListener= null; fDocumentListener= null; } }
Buffer validation state based on modification stamp.
/** * Buffer validation state based on modification stamp. */
class ModificationStampValidationState extends BufferValidationState { private ModificationStamp fModificationStamp; public ModificationStampValidationState(IFile file) { super(file); fModificationStamp= getModificationStamp(); } @Override public RefactoringStatus isValid(boolean needsSaving, boolean resilientForDerived) throws CoreException { RefactoringStatus result= super.isValid(needsSaving, resilientForDerived); if (result.hasFatalError()) return result; ModificationStamp currentStamp= getModificationStamp(); // we don't need to check the kind here since the document stamp // and file stamp are in sync for documents implementing // IDocumentExtension4. If both are file stamps the file must // not be dirty if (fModificationStamp.getValue() != currentStamp.getValue() // we know here that the stamp value are equal. However, if // the stamp is a null stamp then the king must be equal as well. || (fModificationStamp.isFileStamp() && fModificationStamp.getValue() == IResource.NULL_STAMP && !currentStamp.isFileStamp()) || (fModificationStamp.isDocumentStamp() && fModificationStamp.getValue() == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP && !currentStamp.isDocumentStamp()) || (fModificationStamp.isFileStamp() && currentStamp.isFileStamp() && isDirty(fFile))) { result.addFatalError(Messages.format( RefactoringCoreMessages.TextChanges_error_content_changed, BasicElementLabels.getPathLabel(fFile.getFullPath(), false) )); } return result; } } /* class SavedBufferValidationState extends BufferValidationState { private long fModificationStamp; public SavedBufferValidationState(IFile file) { super(file); fModificationStamp= file.getModificationStamp(); } public RefactoringStatus isValid(boolean needsSaving) { RefactoringStatus result= super.isValid(needsSaving); if (result.hasFatalError()) return result; ModificationStamp currentStamp= getModificationStamp(); if (fModificationStamp != currentStamp.value) { result.addFatalError(Messages.format( RefactoringCoreMessages.TextChanges_error_content_changed, //$NON-NLS-1$ fFile.getFullPath().toString() )); } else if (fFile.isReadOnly()) { result.addFatalError(Messages.format( RefactoringCoreMessages.TextChanges_error_read_only, //$NON-NLS-1$ fFile.getFullPath().toString() )); } else if (!fFile.isSynchronized(IResource.DEPTH_ZERO)) { result.addFatalError(Messages.format( RefactoringCoreMessages.TextChanges_error_outOfSync, //$NON-NLS-1$ fFile.getFullPath().toString() )); } else if (isDirty(fFile) && currentStamp.isFileStamp()){ result.addFatalError(Messages.format( RefactoringCoreMessages.TextChanges_error_unsaved_changes, //$NON-NLS-1$ fFile.getFullPath().toString() )); } return result; } } */