blob: 0eabf5b37246b88ef25380aac17eb2f72391e039 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2020 1C-Soft LLC and others.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Vladimir Piskarev (1C) - initial API and implementation
*******************************************************************************/
package org.eclipse.handly.ui.texteditor;
import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.model.Elements;
import org.eclipse.handly.model.ISourceFile;
import org.eclipse.handly.model.impl.ISourceFileImplExtension;
import org.eclipse.handly.ui.IWorkingCopyManager;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.texteditor.IDocumentProvider;
/**
* Subclass of {@link TextFileDocumentProvider} specialized for
* working copy management of source files.
*/
public abstract class SourceFileDocumentProvider
extends TextFileDocumentProvider
implements IWorkingCopyManager
{
private static final ISourceFile[] NO_WORKING_COPIES = new ISourceFile[0];
/**
* Creates a new source file document provider with no parent.
*/
public SourceFileDocumentProvider()
{
this(null);
}
/**
* Creates a new source file document provider with the given parent.
*
* @param parent the parent document provider
*/
public SourceFileDocumentProvider(IDocumentProvider parent)
{
super(parent);
}
/**
* {@inheritDoc}
* <p>
* This implementation returns the working copy retained by the file info for
* the given element. The file info is obtained via {@link #getFileInfo(Object)}.
* </p>
*/
@Override
public ISourceFile getWorkingCopy(Object element)
{
FileInfo info = getFileInfo(element);
if (info instanceof SourceFileInfo)
return ((SourceFileInfo)info).workingCopy;
return null;
}
/**
* {@inheritDoc}
* <p>
* This implementation returns the working copy retained by the file info
* for the given document. The file info is found by iterating over
* this provider's file info objects via {@link #getFileInfosIterator()}
* and testing whether the document of the file info's text file buffer
* equals the given document.
* </p>
*/
@Override
public ISourceFile getWorkingCopy(IDocument document)
{
Iterator<FileInfo> it = getFileInfosIterator();
while (it.hasNext())
{
FileInfo info = it.next();
IDocument infoDocument = null;
if (info.fTextFileBuffer != null)
infoDocument = info.fTextFileBuffer.getDocument();
if (infoDocument != null && infoDocument.equals(document))
{
if (info instanceof SourceFileInfo)
return ((SourceFileInfo)info).workingCopy;
}
}
return null;
}
/**
* {@inheritDoc}
* <p>
* This implementation iterates over this provider's file info objects
* via {@link #getFileInfosIterator()} and collects the working copies
* they retain.
* </p>
*/
@Override
public ISourceFile[] getWorkingCopies()
{
List<ISourceFile> result = new ArrayList<>();
Iterator<FileInfo> it = getFileInfosIterator();
while (it.hasNext())
{
FileInfo info = it.next();
if (info instanceof SourceFileInfo)
{
ISourceFile workingCopy = ((SourceFileInfo)info).workingCopy;
if (workingCopy != null)
result.add(workingCopy);
}
}
return result.toArray(NO_WORKING_COPIES);
}
/**
* Returns the source file for the given element.
*
* @param element the element
* @return the source file for the given element,
* or <code>null</code> if none
*/
protected abstract ISourceFile getSourceFile(Object element);
/**
* {@inheritDoc}
* <p>
* This implementation returns a new instance of
* {@link SourceFileDocumentProvider.SourceFileInfo SourceFileInfo}.
* </p>
*/
@Override
protected FileInfo createEmptyFileInfo()
{
return new SourceFileInfo();
}
/**
* {@inheritDoc}
* <p>
* This implementation invokes the superclass implementation to create the
* file info object. It then attempts to {@link #acquireWorkingCopy(Object,
* TextFileDocumentProvider.FileInfo) acquire} a working copy for the
* given element if the created object is an instance of {@link
* SourceFileDocumentProvider.SourceFileInfo SourceFileInfo} and,
* if successful, stores a reference to the acquired working copy
* in the created file info.
* </p>
*/
@Override
protected FileInfo createFileInfo(Object element) throws CoreException
{
FileInfo info = super.createFileInfo(element);
if (info instanceof SourceFileInfo)
{
boolean f = false;
try
{
ISourceFile workingCopy = acquireWorkingCopy(element, info);
if (workingCopy != null)
{
if (!Elements.isWorkingCopy(workingCopy))
throw new AssertionError();
boolean f2 = false;
try (IBuffer buffer = Elements.getBuffer(workingCopy))
{
IDocument document = null;
if (info.fTextFileBuffer != null)
document = info.fTextFileBuffer.getDocument();
if (!buffer.getDocument().equals(document))
throw new AssertionError();
f2 = true;
}
finally
{
if (!f2)
releaseWorkingCopy(workingCopy, element, info);
}
((SourceFileInfo)info).workingCopy = workingCopy;
}
f = true;
}
finally
{
if (!f)
super.disposeFileInfo(element, info);
}
}
return info;
}
/**
* {@inheritDoc}
* <p>
* This implementation invokes the superclass implementation after trying to
* {@link #releaseWorkingCopy(ISourceFile, Object, TextFileDocumentProvider.FileInfo)
* release} the working copy retained by the given file info object.
* </p>
*/
@Override
protected void disposeFileInfo(Object element, FileInfo info)
{
try
{
if (info instanceof SourceFileInfo)
{
ISourceFile workingCopy = ((SourceFileInfo)info).workingCopy;
if (workingCopy != null)
releaseWorkingCopy(workingCopy, element, info);
}
}
finally
{
super.disposeFileInfo(element, info);
}
}
/**
* Attempts to acquire a working copy for the given element. A working copy
* acquired by this method <b>must</b> be released eventually via a call to
* {@link #releaseWorkingCopy(ISourceFile, Object, TextFileDocumentProvider.FileInfo)
* releaseWorkingCopy}.
* <p>
* This implementation obtains a source file for the given element via
* {@link #getSourceFile(Object)} and, if the source file implements
* {@link ISourceFileImplExtension}, invokes <code>{@link
* ISourceFileImplExtension#becomeWorkingCopy_ becomeWorkingCopy_}(EMPTY_CONTEXT,
* null)</code> on it and returns the acquired working copy.
* Otherwise, <code>null</code> is returned.
* </p>
*
* @param element the element
* @param info the element info
* @return the acquired working copy, or <code>null</code> if no working copy
* can be acquired for the given element
* @throws CoreException if the working copy could not be acquired successfully
*/
protected ISourceFile acquireWorkingCopy(Object element, FileInfo info)
throws CoreException
{
ISourceFile sourceFile = getSourceFile(element);
if (sourceFile instanceof ISourceFileImplExtension)
{
((ISourceFileImplExtension)sourceFile).becomeWorkingCopy_(
EMPTY_CONTEXT, null);
return sourceFile;
}
return null;
}
/**
* Releases the given working copy that was acquired via a call to
* {@link #acquireWorkingCopy(Object, TextFileDocumentProvider.FileInfo)
* acquireWorkingCopy}.
* <p>
* This implementation invokes <code>((ISourceFileImplExtension)workingCopy).{@link
* ISourceFileImplExtension#releaseWorkingCopy_() releaseWorkingCopy_()}</code>.
* </p>
*
* @param workingCopy the working copy to release
* @param element the element
* @param info the element info
*/
protected void releaseWorkingCopy(ISourceFile workingCopy, Object element,
FileInfo info)
{
((ISourceFileImplExtension)workingCopy).releaseWorkingCopy_();
}
/**
* Subclass of {@link org.eclipse.ui.editors.text.TextFileDocumentProvider.FileInfo
* FileInfo} that can retain a reference to a working copy of a source file.
*/
protected static class SourceFileInfo
extends FileInfo
{
ISourceFile workingCopy;
}
}