blob: 70fb15aba1770985676849e9fc125b7f34179dbd [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2020 Stephan Wahlbrink 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, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.model.core.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.statet.ecommons.io.FileUtil;
import org.eclipse.statet.internal.ltk.core.LTKCorePlugin;
import org.eclipse.statet.ltk.core.SourceContent;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
/**
* Common implementation of {@link IWorkingBuffer} for source units based on
* an {@link IFile} or an {@link IFileStore}.
*/
public class WorkingBuffer implements IWorkingBuffer {
/** Mode for IFile (in workspace) */
protected static final byte IFILE= 1;
/** Mode for IFileStore (URI) */
protected static final byte FILESTORE= 2;
protected static final byte DOCUMENT= 1;
public static SourceContent createContentFromDocument(final IDocument doc) {
Object lock= null;
if (doc instanceof ISynchronizable) {
lock= ((ISynchronizable) doc).getLockObject();
}
if (lock != null && doc instanceof IDocumentExtension4) {
synchronized (lock) {
return new SourceContent(
((IDocumentExtension4) doc).getModificationStamp(),
doc.get() );
}
}
else {
return new SourceContent(System.currentTimeMillis(), doc.get());
}
}
protected final ISourceUnit unit;
private AbstractDocument document;
/**
* Mode of this working buffer:<ul>
* <li>= 0 - uninitialized</li>
* <li>< 0 - invalid/no source found</li>
* <li>> 0 - mode constant {@link #IFILE}, {@link #FILESTORE}</li>
* </ul>
*/
private byte mode;
public WorkingBuffer(final ISourceUnit unit) {
this.unit= unit;
}
/**
* Checks the mode of this working buffer
*
* @return <code>true</code> if valid mode, otherwise <code>false</code>
*/
protected final byte detectResourceMode() {
if (this.mode == 0) {
final Object resource= this.unit.getResource();
if (resource instanceof IFile) {
this.mode= IFILE;
}
else if (resource instanceof IFileStore
&& !((IFileStore) resource).fetchInfo().isDirectory() ) {
this.mode= FILESTORE;
}
if (this.mode == 0) {
this.mode= -1;
}
}
return this.mode;
}
protected final byte getResourceMode() {
return this.mode;
}
protected byte getContentMode() {
return 0;
}
@Override
public long getContentStamp(final IProgressMonitor monitor) {
{ final AbstractDocument doc= this.document;
if (doc != null) {
return doc.getModificationStamp();
}
}
{ final ISourceUnit underlyingUnit= this.unit.getUnderlyingUnit();
if (underlyingUnit != null) {
return underlyingUnit.getContentStamp(monitor);
}
}
if (detectResourceMode() == IFILE) {
final IFile resource= (IFile) this.unit.getResource();
if (resource != null) {
return resource.getModificationStamp();
}
}
return 0;
}
@Override
public synchronized AbstractDocument getDocument() {
return this.document;
}
@Override
public synchronized AbstractDocument getDocument(final IProgressMonitor monitor) {
AbstractDocument doc= this.document;
if (doc == null) {
final SubMonitor m= SubMonitor.convert(monitor);
doc= createDocument(m);
checkDocument(doc);
if ((getContentMode() & DOCUMENT) != 0) {
this.document= doc;
}
}
return doc;
}
@Override
public SourceContent getContent(final IProgressMonitor monitor) {
final SubMonitor m= SubMonitor.convert(monitor);
final IDocument doc= ((getContentMode() & DOCUMENT) != 0) ? getDocument(monitor) : getDocument();
if (doc != null) {
return createContentFromDocument(doc);
}
return createContent(m);
}
@Override
public void saveDocument(final IProgressMonitor monitor) {
}
@Override
public synchronized void releaseDocument(final IProgressMonitor monitor) {
this.document= null;
}
@Override
public boolean checkState(final boolean validate, final IProgressMonitor monitor) {
{ final ISourceUnit underlyingUnit= this.unit.getUnderlyingUnit();
if (underlyingUnit != null) {
return underlyingUnit.checkState(validate, monitor);
}
}
switch (detectResourceMode()) {
case IFILE:
{ final IFile resource= (IFile) this.unit.getResource();
if (!validate) {
return !resource.getResourceAttributes().isReadOnly();
}
else {
return resource.getWorkspace().validateEdit(new IFile[] { resource }, IWorkspace.VALIDATE_PROMPT).isOK();
}
}
case FILESTORE:
{ final IFileStore store= (IFileStore) this.unit.getResource();
try {
return !store.fetchInfo(EFS.NONE, monitor).getAttribute(EFS.ATTRIBUTE_READ_ONLY);
}
catch (final CoreException e) {
LTKCorePlugin.log(new Status(IStatus.ERROR, LTKCorePlugin.BUNDLE_ID, 0,
"An error occurred when checking modifiable state of the file.", e));
return false;
}
}
default:
return false;
}
}
protected AbstractDocument createDocument(final SubMonitor m) {
final IDocument fileDoc= createEmptyDocument();
if (!(fileDoc instanceof AbstractDocument)) {
return null;
}
final AbstractDocument document= (AbstractDocument) fileDoc;
final ISourceUnit underlyingUnit= this.unit.getUnderlyingUnit();
if (underlyingUnit != null) {
final SourceContent underlyingContent= underlyingUnit.getContent(m);
// if (document instanceof IDocumentExtension4) {
document.set(underlyingContent.getText(), underlyingContent.getStamp());
// }
// else {
// document.set(underlyingContent.text);
// }
}
else {
final Object resource= this.unit.getResource();
if (resource instanceof IFile) {
loadDocumentFromFile((IFile) resource, document, m);
}
}
return document;
}
private IDocument createEmptyDocument() {
switch (detectResourceMode()) {
case IFILE:
return FileBuffers.getTextFileBufferManager().createEmptyDocument(
((IFile) this.unit.getResource()).getFullPath(),
LocationKind.IFILE );
case FILESTORE:
return FileBuffers.getTextFileBufferManager().createEmptyDocument(
URIUtil.toPath(((IFileStore) this.unit.getResource()).toURI()),
LocationKind.LOCATION );
default:
return FileBuffers.getTextFileBufferManager().createEmptyDocument(null, null);
}
}
protected void checkDocument(final AbstractDocument document) {
if (document instanceof ISynchronizable) {
synchronized (document) {
if (((ISynchronizable) document).getLockObject() == null) {
((ISynchronizable) document).setLockObject(new Object());
}
}
}
}
protected final void loadDocumentFromFile(final IFile file, final AbstractDocument document, final SubMonitor m) {
try {
FileUtil.getFileUtil(file).createReadTextFileOp(new FileUtil.ReaderAction() {
@Override
public void run(final BufferedReader reader, final IProgressMonitor monitor) throws IOException {
final StringBuilder buffer= new StringBuilder();
final char[] readBuffer= new char[2048];
int n;
while ((n= reader.read(readBuffer)) > 0) {
buffer.append(readBuffer, 0, n);
}
// if (document instanceof IDocumentExtension4) {
document.set(buffer.toString(), file.getModificationStamp());
// }
// else {
// document.set(buffer.toString());
// }
}
}).doOperation(m);
}
catch (final OperationCanceledException e) {
}
catch (final CoreException e) {
LTKCorePlugin.log(e.getStatus());
}
}
protected SourceContent createContent(final SubMonitor m) {
final ISourceUnit underlyingUnit= this.unit.getUnderlyingUnit();
if (underlyingUnit != null) {
return underlyingUnit.getContent(m);
}
else {
final Object resource= this.unit.getResource();
final AtomicReference<SourceContent> content= new AtomicReference<>();
if (resource instanceof IFile) {
loadContentFromFile((IFile) resource, content, m);
}
return content.get();
}
}
protected final void loadContentFromFile(final IFile file, final AtomicReference<SourceContent> content, final SubMonitor m) {
try {
FileUtil.getFileUtil(file).createReadTextFileOp(new FileUtil.ReaderAction() {
@Override
public void run(final BufferedReader reader, final IProgressMonitor monitor) throws IOException {
final StringBuilder buffer= new StringBuilder();
final char[] readBuffer= new char[2048];
int n;
while ((n= reader.read(readBuffer)) >= 0) {
buffer.append(readBuffer, 0, n);
}
content.set(new SourceContent(file.getModificationStamp(), buffer.toString()));
}
}).doOperation(m);
}
catch (final OperationCanceledException e) {
}
catch (final CoreException e) {
LTKCorePlugin.log(e.getStatus());
}
}
@Override
public boolean isSynchronized() {
return false;
}
}