/*******************************************************************************
* Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
* William Chen (Wind River)- [345552] Edit the remote files with a proper editor
*******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.ui.internal.compare;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.compare.IEditableContent;
import org.eclipse.compare.ISharedDocumentAdapter;
import org.eclipse.compare.ITypedElement;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime.IFSTreeNode;
import org.eclipse.tcf.te.tcf.filesystem.ui.activator.UIPlugin;
import org.eclipse.tcf.te.tcf.filesystem.ui.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
import org.eclipse.tcf.te.tcf.filesystem.ui.nls.Messages;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.ide.FileStoreEditorInput;
/**
* A <code>LocalTypedElement</code> extends <code>MergeTypedElement</code> and
* wraps an <code>IFSTreeNode</code> so that it can be used as the left element
* of a <code>MergeEditorInput</code>. It implements the interface
* <code>IEditableContent</code> so that it is editable.
*/
public class LocalTypedElement extends MergeTypedElement implements
IEditableContent, IAdaptable, ISharedDocumentAdapterListener {
// If the current edited file is dirty.
private boolean dirty;
// The shared document adapter
private EditableSharedDocumentAdapter documentAdapter;
// The shared document listener.
private ISharedDocumentAdapterListener documentListener;
/**
* Creates a <code>LocalTypedElement</code> for the given resource.
*
*/
public LocalTypedElement(IFSTreeNode node) {
super(node);
setContent(getContent());
dirty = false;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
if (adapter == ISharedDocumentAdapter.class) {
if (documentAdapter == null)
documentAdapter = new EditableSharedDocumentAdapter(this);
return documentAdapter;
}
return Platform.getAdapterManager().getAdapter(this, adapter);
}
/* (non-Javadoc)
* @see org.eclipse.compare.BufferedContent#setContent(byte[])
*/
@Override
public void setContent(byte[] contents) {
dirty = true;
super.setContent(contents);
}
/**
* Set the document listener.
*
* @param documentListener
* the document listener.
*/
public void setDocumentListener(
ISharedDocumentAdapterListener documentListener) {
this.documentListener = documentListener;
}
/**
* Return an input stream that opens that locally cached file to provide the
* content.
*
* @return a buffered input stream containing the contents of this file
* @exception CoreException
* if the contents of this storage could not be accessed
*/
@Override
protected InputStream createStream() throws CoreException {
try {
File cacheFile = node.getCacheFile();
return new BufferedInputStream(new FileInputStream(cacheFile));
} catch (FileNotFoundException e) {
IStatus error = new Status(IStatus.ERROR,
UIPlugin.getUniqueIdentifier(), e.getMessage(), e);
throw new CoreException(error);
}
}
/* (non-Javadoc)
* @see org.eclipse.compare.IEditableContent#isEditable()
*/
@Override
public boolean isEditable() {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.compare.IEditableContent#replace(org.eclipse.compare.ITypedElement, org.eclipse.compare.ITypedElement)
*/
@Override
public ITypedElement replace(ITypedElement dest, ITypedElement src) {
return dest;
}
/**
* Save the shared document for this element. The save can only be performed
* if the element is connected to a shared document. If the element is not
* connected, <code>false</code> is returned.
*
* @param monitor
* a progress monitor
* @throws CoreException
*/
public boolean store2Document(IProgressMonitor monitor)
throws CoreException {
if (isConnected()) {
IEditorInput input = documentAdapter.getDocumentKey(this);
documentAdapter.saveDocument(input, monitor);
return true;
}
return false;
}
/**
* Judges whether the content has been changed.
*
* @return
*/
public boolean isDirty() {
return dirty
|| (documentAdapter != null && documentAdapter
.hasBufferedContents());
}
/**
* Set the dirty action.
*
* @param dirty
* The dirty action.
*/
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
/**
* If the document adapter has been connected.
*
* @return true if it is not null and connected.
*/
public boolean isConnected() {
return documentAdapter != null && documentAdapter.isConnected();
}
/**
* Return the path to the local file of this node. It is used to compute its
* hash code and as the title of the comparison editor.
*/
@Override
public String toString() {
File cacheFile = node.getCacheFile();
return cacheFile.toString();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentConnected()
*/
@Override
public void handleDocumentConnected() {
if (documentListener != null)
documentListener.handleDocumentConnected();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentDeleted()
*/
@Override
public void handleDocumentDeleted() {
if (documentListener != null)
documentListener.handleDocumentDeleted();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentDisconnected()
*/
@Override
public void handleDocumentDisconnected() {
if (documentListener != null)
documentListener.handleDocumentDisconnected();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentFlushed()
*/
@Override
public void handleDocumentFlushed() {
fireContentChanged();
if (documentListener != null)
documentListener.handleDocumentFlushed();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentSaved()
*/
@Override
public void handleDocumentSaved() {
if (documentListener != null)
documentListener.handleDocumentSaved();
}
/**
* Get an editor input for this file.
*
* @return The editor input.
*/
public IEditorInput getEditorInput() {
IPath path = new Path(node.getCacheFile().getAbsolutePath());
IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
return new FileStoreEditorInput(fileStore);
}
/**
* Save to its local file when the document has not been connected yet.
*
* @param monitor
* The monitor that reports the progress.
*/
public void store2Cache(IProgressMonitor monitor) throws CoreException {
File cacheFile = node.getCacheFile();
monitor.beginTask(NLS.bind(Messages.LocalTypedElement_SavingFile, cacheFile.getName()), 100);
InputStream is = getContents();
BufferedOutputStream bos = null;
try {
long total = cacheFile.length();
bos = new BufferedOutputStream(new FileOutputStream(cacheFile));
byte[] data = new byte[10 * 1024];
int length;
long current = 0;
int currProgress = 0;
while ((length = is.read(data)) > 0) {
bos.write(data, 0, length);
bos.flush();
current += length;
int progress = (int) (current * 100 / total);
if (currProgress != progress) {
monitor.worked(progress - currProgress);
currProgress = progress;
}
}
setDirty(false);
} catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR,
UIPlugin.getUniqueIdentifier(), e.getMessage(), e));
} finally {
if (is != null)
try {
is.close();
} catch (IOException ex) {
}
if (bos != null) {
try {
bos.close();
} catch (Exception e) {
}
}
// Notify the local file element that the document has changed.
fireContentChanged();
}
}
}