Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf')
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/activator/CorePlugin.java93
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IConfirmCallback.java42
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IOperation.java61
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IWindowsFileAttributes.java97
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/runtime/IRuntimeModel.java28
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/steps/IFileSystemStepAttributes.java26
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/UserAccount.java95
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/CallbackBase.java35
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpen.java82
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpenChannel.java93
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneReadDir.java96
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneRoots.java64
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneOpenChannel.java88
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneStat.java111
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFChannelException.java44
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFException.java73
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFFileSystemException.java45
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/IOpExecutor.java28
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/JobExecutor.java100
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/NullOpExecutor.java79
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheFileDigest.java112
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheUpdate.java76
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpClipboard.java113
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCommitAttr.java85
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCopy.java258
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreate.java192
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFile.java81
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFolder.java72
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDelete.java232
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDownload.java206
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpMove.java205
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpOutStreamOp.java98
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpParsePath.java227
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefresh.java193
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefreshRoots.java77
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRename.java125
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpStreamOp.java74
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpTargetFileDigest.java110
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java431
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUser.java167
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/Operation.java618
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/FSTreeNodePropertyTester.java116
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/TargetPropertyTester.java68
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfInputStream.java148
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfOutputStream.java119
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLConnection.java306
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLStreamHandlerService.java216
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/BlockingFileSystemProxy.java880
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/CacheManager.java204
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/ContentTypeHelper.java120
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/FileState.java257
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManager.java203
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManagerDelegate.java289
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/AbstractTreeNode.java342
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/CacheState.java22
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/FSTreeNode.java681
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/ModelManager.java108
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/RuntimeModel.java228
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/UserAccount.java95
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.java121
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.properties100
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/services/FileTransferService.java523
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/DisposeModelStep.java45
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/FileTransferStep.java79
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/InitializeModelStep.java71
65 files changed, 10473 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/activator/CorePlugin.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/activator/CorePlugin.java
new file mode 100644
index 000000000..35a9a4623
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/activator/CorePlugin.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.activator;
+
+import java.util.Hashtable;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLConnection;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLStreamHandlerService;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+
+/**
+ * The activator class of the core file system plugin.
+ */
+public class CorePlugin extends Plugin {
+
+ // The bundle context of this plugin.
+ private static BundleContext context;
+ // The shared instance of this plug-in.
+ private static CorePlugin plugin;
+ // The service registration for the "tcf" URL stream handler.
+ private ServiceRegistration<?> regURLStreamHandlerService;
+
+ /**
+ * Get the bundle context of this plugin.
+ * @return The bundle context object.
+ */
+ static BundleContext getContext() {
+ return context;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ super.start(bundleContext);
+ CorePlugin.context = bundleContext;
+ plugin = this;
+ // Register the "tcf" URL stream handler service.
+ Hashtable<String, String[]> properties = new Hashtable<String, String[]>();
+ properties.put(URLConstants.URL_HANDLER_PROTOCOL, new String[] { TcfURLConnection.PROTOCOL_SCHEMA });
+ regURLStreamHandlerService = context.registerService(URLStreamHandlerService.class.getName(), new TcfURLStreamHandlerService(), properties);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ if (PersistenceManager.needsDisposal()) PersistenceManager.getInstance().dispose();
+ if (regURLStreamHandlerService != null) {
+ // When URL stream handler service is unregistered, any URL related operation will be invalid.
+ regURLStreamHandlerService.unregister();
+ regURLStreamHandlerService = null;
+ }
+ CorePlugin.context = null;
+ plugin = null;
+ super.stop(bundleContext);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CorePlugin getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Convenience method which returns the unique identifier of this plugin.
+ */
+ public static String getUniqueIdentifier() {
+ if (getContext() != null && getContext().getBundle() != null) {
+ return getContext().getBundle().getSymbolicName();
+ }
+ return "org.eclipse.tcf.te.tcf.filesystem.core"; //$NON-NLS-1$
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IConfirmCallback.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IConfirmCallback.java
new file mode 100644
index 000000000..844b1416c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IConfirmCallback.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.interfaces;
+
+/**
+ * A confirmation callback used to get UI confirmation from user for a long process
+ */
+public interface IConfirmCallback {
+ // Yes button ID.
+ int YES = 0;
+ // Yes to All button ID.
+ int YES_TO_ALL = 1;
+ // No to All button ID.
+ int NO = 2;
+ // Cancel button ID.
+ int CANCEL = 3;
+ // OK button ID.
+ int OK = YES;
+ // No to all button ID.
+ int NO_TO_ALL = 4;
+ /**
+ * Test if the given object requires confirmation.
+ *
+ * @param object The object being tested.
+ * @return true if it requires confirmation.
+ */
+ boolean requires(Object object);
+ /**
+ * Confirm with the user weather the process should continue, continue for all, skip or cancel.
+ *
+ * @param object The object being tested.
+ * @return a button ID the user selects during confirmation.
+ */
+ int confirms(Object object);
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IOperation.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IOperation.java
new file mode 100644
index 000000000..99a5a4ed1
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IOperation.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.interfaces;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A class that implement this interface represents an file system operation,
+ * which is an abstract of the action operated over files/folders.
+ */
+public interface IOperation {
+
+ /**
+ * The algorithm of calculating the message digest of a file.
+ */
+ public static final String MD_ALG = "MD5"; //$NON-NLS-1$
+
+ /**
+ * Runs this operation. Progress should be reported to the given progress monitor.
+ * A request to cancel the operation should be honored and acknowledged
+ * by throwing <code>InterruptedException</code>.
+ *
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancellation
+ * @exception InvocationTargetException if the run method must propagate a checked exception,
+ * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
+ * wrapped in an <code>InvocationTargetException</code> by the calling context
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
+ * <code>InterruptedException</code>
+ *
+ */
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException;
+
+ /**
+ * Get the operation's name. This name will be used as the task name of
+ * the given monitor.
+ *
+ * @see IProgressMonitor#beginTask(String, int)
+ * @return The name of the operation.
+ */
+ public String getName();
+
+ /**
+ * Get the total amount of work which will used by the progress
+ * monitor to set the total work.
+ *
+ * @see IProgressMonitor#beginTask(String, int)
+ * @return The total amount of work or UNKNOWN if it is in-determinant
+ */
+ public int getTotalWork();
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IWindowsFileAttributes.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IWindowsFileAttributes.java
new file mode 100644
index 000000000..f333e54f3
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/IWindowsFileAttributes.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.interfaces;
+
+/**
+ * Windows specific file system attribute definitions.
+ *
+ * @see <nop>Windows File Attribute Constants for more details.
+ */
+public interface IWindowsFileAttributes {
+
+ /**
+ * If set, the file is read-only. Read-only files cannot be modified or deleted.
+ * The attributes does not apply to directories.
+ */
+ public int FILE_ATTRIBUTE_READONLY = 1;
+
+ /**
+ * If set, the file or directory is hidden. Hidden files or directories should not
+ * be included in default directory content lists.
+ */
+ public int FILE_ATTRIBUTE_HIDDEN = 2;
+
+ /**
+ * If set, the file or directory is reserved to be used by the OS.
+ */
+ public int FILE_ATTRIBUTE_SYSTEM = 4;
+
+ /**
+ * The file system object is a directory.
+ */
+ public int FILE_ATTRIBUTE_DIRECTORY = 16;
+
+ /**
+ * If set, the file or directory is an archive file or directory.
+ */
+ public int FILE_ATTRIBUTE_ARCHIVE = 32;
+
+ /**
+ * Reserved for system use.
+ */
+ public int FILE_ATTRIBUTE_DEVICE = 64;
+
+ /**
+ * The file system object is a file with no other attributes set. Valid
+ * only if used exclusively.
+ */
+ public int FILE_ATTRIBUTE_NORMAL = 128;
+
+ /**
+ * If set, the file is used for temporary storage.
+ */
+ public int FILE_ATTRIBUTE_TEMPORARY = 256;
+
+ /**
+ * The file is a sparse file.
+ */
+ public int FILE_ATTRIBUTE_SPARSE_FILE = 512;
+
+ /**
+ * If set, the file or directory has an associated reparse point or is a symbolic link.
+ */
+ public int FILE_ATTRIBUTE_REPARSE_POINT = 1024;
+
+ /**
+ * If set, the file or directory is compressed.
+ */
+ public int FILE_ATTRIBUTE_COMPRESSED = 2048;
+
+ /**
+ * If set, the content of the file is currently not available.
+ * This attribute should not be changed by applications.
+ */
+ public int FILE_ATTRIBUTE_OFFLINE = 4096;
+
+ /**
+ * If set, the file or directory is not indexed.
+ */
+ public int FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192;
+
+ /**
+ * If set, the file or directory is encrypted.
+ */
+ public int FILE_ATTRIBUTE_ENCRYPTED = 16384;
+
+ /**
+ * Reserved for system use.
+ */
+ public int FILE_ATTRIBUTE_VIRTUAL = 65536;
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/runtime/IRuntimeModel.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/runtime/IRuntimeModel.java
new file mode 100644
index 000000000..c89068eb4
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/runtime/IRuntimeModel.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime;
+
+import org.eclipse.tcf.te.tcf.core.model.interfaces.IModel;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProvider;
+
+
+/**
+ * A model dealing with the filesystem at runtime.
+ */
+public interface IRuntimeModel extends IModel, IPeerNodeProvider {
+
+ /**
+ * Get the root node of the peer model.
+ *
+ * @return The root node.
+ */
+ public FSTreeNode getRoot();
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/steps/IFileSystemStepAttributes.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/steps/IFileSystemStepAttributes.java
new file mode 100644
index 000000000..8801a4c7e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/interfaces/steps/IFileSystemStepAttributes.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.interfaces.steps;
+
+/**
+ * Defines filesystem related step data attribute id's.
+ */
+public interface IFileSystemStepAttributes {
+
+ /**
+ * Define the prefix used by all other attribute id's as prefix.
+ */
+ public static final String ATTR_PREFIX = "org.eclipse.tcf.te.tcf.filesystem.core"; //$NON-NLS-1$
+
+ /**
+ * The file transfer item the stepper is currently operating with.
+ */
+ public static final String ATTR_FILE_TRANSFER_ITEM = ATTR_PREFIX + ".file_transfer_item"; //$NON-NLS-1$
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/UserAccount.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/UserAccount.java
new file mode 100644
index 000000000..b46ed125a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/UserAccount.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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) - [352302]Opening a file in an editor depending on
+ * the client's permissions.
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal;
+
+/**
+ * The data model of a user account.
+ */
+public class UserAccount {
+ // The user's id.
+ private int uid;
+ // The user's group id.
+ private int gid;
+ // The user's effective id.
+ private int euid;
+ // The user's effective group id.
+ private int egid;
+ // The user's home directory.
+ private String home;
+
+ /**
+ * Create a user account with given data.
+ *
+ * @param uid
+ * The user's id
+ * @param gid
+ * The user's group id
+ * @param euid
+ * The user's effective id.
+ * @param egid
+ * The user's effective group id.
+ * @param home
+ * The user's home directory.
+ */
+ public UserAccount(int uid, int gid, int euid, int egid, String home) {
+ this.uid = uid;
+ this.gid = gid;
+ this.euid = euid;
+ this.egid = egid;
+ this.home = home;
+ }
+
+ /**
+ * Get the user's id.
+ *
+ * @return The user's id.
+ */
+ public int getUID() {
+ return uid;
+ }
+
+ /**
+ * Get the user's group id.
+ *
+ * @return The user's group id.
+ */
+ public int getGID() {
+ return gid;
+ }
+
+ /**
+ * Get the user's effective id.
+ *
+ * @return The user's effective id.
+ */
+ public int getEUID() {
+ return euid;
+ }
+
+ /**
+ * Get the user's effective group id.
+ *
+ * @return The user's effective group id.
+ */
+ public int getEGID() {
+ return egid;
+ }
+
+ /**
+ * Get the user's home directory.
+ *
+ * @return The user's home directory.
+ */
+ public String getHome() {
+ return home;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/CallbackBase.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/CallbackBase.java
new file mode 100644
index 000000000..e85a38595
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/CallbackBase.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import org.eclipse.tcf.protocol.IErrorReport;
+/**
+ * The base class for all callback classes.
+ */
+public class CallbackBase {
+
+ /**
+ * Get the error message from the throwable error.
+ *
+ * @param error The throwable error.
+ * @return The error message.
+ */
+ protected String getErrorMessage(Throwable error) {
+ String message = null;
+ if(error instanceof IErrorReport) {
+ IErrorReport report = (IErrorReport) error;
+ message = (String)report.getAttributes().get(IErrorReport.ERROR_FORMAT);
+ }
+ else {
+ message = error.getMessage();
+ }
+ return message;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpen.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpen.java
new file mode 100644
index 000000000..42ddeb53b
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpen.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneClose;
+import org.eclipse.tcf.services.IFileSystem.DoneOpen;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The callback handler that handles the event that a directory is opened.
+ */
+public class QueryDoneOpen extends CallbackBase implements DoneOpen {
+ // The tcf channel used.
+ IChannel channel;
+ // The file system service.
+ IFileSystem service;
+ // The parent node being queried.
+ FSTreeNode parentNode;
+ // The callback
+ ICallback callback;
+
+ /**
+ * Create an instance with parameters to initialize the fields.
+ *
+ * @param callback the callback
+ * @param channel The tcf channel.
+ * @param service The file system service.
+ * @param node The parent node.
+ */
+ public QueryDoneOpen(ICallback callback, IChannel channel, IFileSystem service, FSTreeNode node) {
+ this.callback = callback;
+ this.channel = channel;
+ this.service = service;
+ this.parentNode = node;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem.DoneOpen#doneOpen(org.eclipse.tcf.protocol.IToken, org.eclipse.tcf.services.IFileSystem.FileSystemException, org.eclipse.tcf.services.IFileSystem.IFileHandle)
+ */
+ @Override
+ public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) {
+ if (error == null) {
+ // Read the directory content until finished
+ service.readdir(handle, new QueryDoneReadDir(callback, channel, service, handle, parentNode));
+ }
+ else if (callback != null) {
+ // Close the handle and channel if EOF is signaled or an error occurred.
+ if (handle != null) {
+ service.close(handle, new DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ IStatus status;
+ if (error == null) status = Status.OK_STATUS;
+ else status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), getErrorMessage(error), error);
+ callback.done(this, status);
+ }
+ });
+ }
+ else {
+ IStatus status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), getErrorMessage(error), error);
+ callback.done(this, status);
+ }
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpenChannel.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpenChannel.java
new file mode 100644
index 000000000..62298c98e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneOpenChannel.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.te.runtime.callback.Callback;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The callback handler that handles the event when the channel opens.
+ */
+public class QueryDoneOpenChannel extends CallbackBase implements DoneOpenChannel {
+ // The parent node to be queried.
+ FSTreeNode parentNode;
+ // Callback object.
+ ICallback callback;
+
+ /**
+ * Create an instance with a parent node.
+ *
+ * @param parentNode The parent node.
+ */
+ public QueryDoneOpenChannel(FSTreeNode parentNode) {
+ this(parentNode, null);
+ }
+
+ /**
+ * Create an instance with a parent node.
+ *
+ * @param parentNode The parent node.
+ * @param callback Callback object.
+ */
+ public QueryDoneOpenChannel(FSTreeNode parentNode, ICallback callback) {
+ this.parentNode = parentNode;
+ this.callback = callback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel#doneOpenChannel(java.lang.Throwable, org.eclipse.tcf.protocol.IChannel)
+ */
+ @Override
+ public void doneOpenChannel(Throwable error, final IChannel channel) {
+ Assert.isTrue(Protocol.isDispatchThread());
+ if(error == null) {
+ ICallback proxy = new Callback(){
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Reset the children query markers
+ parentNode.queryDone();
+ Tcf.getChannelManager().closeChannel(channel);
+ if(callback != null) {
+ callback.done(caller, status);
+ }
+ }
+ };
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if(service != null) {
+ if(parentNode.isSystemRoot()) {
+ service.roots(new QueryDoneRoots(proxy, parentNode));
+ } else {
+ String absPath = parentNode.getLocation();
+ service.opendir(absPath, new QueryDoneOpen(proxy, channel, service, parentNode));
+ }
+ } else {
+ Status status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), Messages.Operation_NoFileSystemError, null);
+ proxy.done(this, status);
+ }
+ }
+ else if(!(error instanceof OperationCanceledException) && callback != null) {
+ IStatus status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), getErrorMessage(error), error);
+ callback.done(this, status);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneReadDir.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneReadDir.java
new file mode 100644
index 000000000..35b8ad64e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneReadDir.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DirEntry;
+import org.eclipse.tcf.services.IFileSystem.DoneClose;
+import org.eclipse.tcf.services.IFileSystem.DoneReadDir;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The callback handler that handles the event when a directory is read.
+ */
+public class QueryDoneReadDir extends CallbackBase implements DoneReadDir {
+ // The tcf channel.
+ IChannel channel;
+ // The file system service.
+ IFileSystem service;
+ // The file handle of the parent directory.
+ IFileSystem.IFileHandle handle;
+ // The parent node being queried.
+ FSTreeNode parentNode;
+ // The callback object.
+ ICallback callback;
+ /**
+ * Create an instance with parameters to initialize the fields.
+ *
+ * @param channel The tcf channel.
+ * @param service The file system service.
+ * @param handle The directory's file handle.
+ * @param parentNode The parent directory.
+ */
+ public QueryDoneReadDir(ICallback callback, IChannel channel, IFileSystem service, IFileSystem.IFileHandle handle, FSTreeNode parentNode) {
+ this.callback = callback;
+ this.channel = channel;
+ this.service = service;
+ this.handle = handle;
+ this.parentNode = parentNode;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem.DoneReadDir#doneReadDir(org.eclipse.tcf.protocol.IToken, org.eclipse.tcf.services.IFileSystem.FileSystemException, org.eclipse.tcf.services.IFileSystem.DirEntry[], boolean)
+ */
+ @Override
+ public void doneReadDir(IToken token, FileSystemException error, DirEntry[] entries, boolean eof) {
+ // Process the returned data
+ if (error == null) {
+ if (entries != null && entries.length > 0) {
+ for (DirEntry entry : entries) {
+ FSTreeNode node = new FSTreeNode(parentNode, entry, false);
+ parentNode.addChild(node);
+ }
+ }
+
+ if (eof) {
+ // Close the handle and channel if EOF is signaled or an error occurred.
+ service.close(handle, new DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ if(callback != null) {
+ IStatus status = error == null ? Status.OK_STATUS : new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), error.getMessage(), error);
+ callback.done(this, status);
+ }
+ }});
+ }
+ else {
+ // And invoke ourself again
+ service.readdir(handle, new QueryDoneReadDir(callback, channel, service, handle, parentNode));
+ }
+ } else if(callback != null) {
+ // Close the handle and channel if EOF is signaled or an error occurred.
+ service.close(handle, new DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ IStatus status = error == null ? Status.OK_STATUS : new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), getErrorMessage(error), error);
+ callback.done(this, status);
+ }
+ });
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneRoots.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneRoots.java
new file mode 100644
index 000000000..70f1967cf
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/QueryDoneRoots.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem.DirEntry;
+import org.eclipse.tcf.services.IFileSystem.DoneRoots;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The callback handler that handles the event when the roots are listed.
+ */
+public class QueryDoneRoots extends CallbackBase implements DoneRoots {
+ // The parent directory node.
+ FSTreeNode parentNode;
+ // The callback object
+ ICallback callback;
+
+ /**
+ * Create an instance with parameters to initialize the fields.
+ *
+ * @param callback the callback.
+ * @param parentNode The parent directory node.
+ */
+ public QueryDoneRoots(ICallback callback, FSTreeNode parentNode) {
+ this.callback = callback;
+ this.parentNode = parentNode;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem.DoneRoots#doneRoots(org.eclipse.tcf.protocol.IToken, org.eclipse.tcf.services.IFileSystem.FileSystemException, org.eclipse.tcf.services.IFileSystem.DirEntry[])
+ */
+ @Override
+ public void doneRoots(IToken token, FileSystemException error, DirEntry[] entries) {
+ if (error == null) {
+ if (entries.length > 0) {
+ for (DirEntry entry : entries) {
+ FSTreeNode node = new FSTreeNode(parentNode, entry, true);
+ parentNode.addChild(node);
+ }
+ }
+ else {
+ parentNode.clearChildren();
+ }
+ }
+ if(callback != null) {
+ IStatus status = error == null ? Status.OK_STATUS : new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), getErrorMessage(error), error);
+ callback.done(this, status);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneOpenChannel.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneOpenChannel.java
new file mode 100644
index 000000000..d194fb97e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneOpenChannel.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The callback to process the channel opened event for refreshing the state of a
+ * file system node.
+ */
+public class RefreshStateDoneOpenChannel extends CallbackBase implements IChannelManager.DoneOpenChannel{
+ // The node to be refreshed.
+ FSTreeNode node;
+ // The callback after the refreshing is done.
+ ICallback callback;
+
+ /**
+ * Create an instance.
+ */
+ public RefreshStateDoneOpenChannel(FSTreeNode node, ICallback callback) {
+ this.node = node;
+ this.callback = callback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel#doneOpenChannel(java.lang.Throwable, org.eclipse.tcf.protocol.IChannel)
+ */
+ @Override
+ public void doneOpenChannel(Throwable error, IChannel channel) {
+ IPeer peer = node.peerNode.getPeer();
+ if (error != null) {
+ if(channel != null) {
+ Tcf.getChannelManager().closeChannel(channel);
+ }
+ if (!(error instanceof OperationCanceledException)) {
+ String message = getErrorMessage(error);
+ IStatus status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), message, error);
+ invokeCallback(status);
+ }
+ }
+ else {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ String path = node.getLocation(true);
+ service.stat(path, new RefreshStateDoneStat(node, channel, callback));
+ }
+ else {
+ Tcf.getChannelManager().closeChannel(channel);
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, peer.getID());
+ IStatus status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), message, new TCFFileSystemException(IStatus.ERROR, message));
+ invokeCallback(status);
+ }
+ }
+ }
+
+ /**
+ * Invoke the callback using the specified status, if the callback
+ * is not null.
+ *
+ * @param status The processing result.
+ */
+ private void invokeCallback(IStatus status) {
+ if(callback != null) {
+ callback.done(this, status);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneStat.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneStat.java
new file mode 100644
index 000000000..aab2d304e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/callbacks/RefreshStateDoneStat.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem.DoneStat;
+import org.eclipse.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.JobExecutor;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpTargetFileDigest;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.FileState;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The callback to process the stat done event when
+ * refreshing the state of a file system node.
+ */
+public class RefreshStateDoneStat extends CallbackBase implements DoneStat {
+ // The channel used to refresh the node's state.
+ IChannel channel;
+ // The node whose state is being refreshed.
+ FSTreeNode node;
+ // The callback to be invoked after refreshing.
+ ICallback callback;
+
+ /**
+ * Create an instance
+ */
+ public RefreshStateDoneStat(FSTreeNode node, IChannel channel, ICallback callback) {
+ this.node = node;
+ this.channel = channel;
+ this.callback = callback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem.DoneStat#doneStat(org.eclipse.tcf.protocol.IToken, org.eclipse.tcf.services.IFileSystem.FileSystemException, org.eclipse.tcf.services.IFileSystem.FileAttrs)
+ */
+ @Override
+ public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) {
+ Tcf.getChannelManager().closeChannel(channel);
+ if (error == null) {
+ FileAttrs oldAttrs = node.attr;
+ node.setAttributes(attrs);
+ // Only update a file's cache diagest.
+ if (node.isFile()) {
+ // Only update its target digest when it has a local cache file.
+ File file = CacheManager.getCacheFile(node);
+ if (file.exists()) {
+ FileState fileDigest = PersistenceManager.getInstance().getFileDigest(node);
+ if (fileDigest.getTargetDigest() == null || (oldAttrs == null && attrs != null || oldAttrs != null && attrs == null || oldAttrs != null && attrs != null && oldAttrs.mtime != attrs.mtime)) {
+ // Its modification time has changed. Update the digest.
+ updateTargetDigest();
+ }
+ else {
+ invokeCallback(Status.OK_STATUS);
+ }
+ }
+ else {
+ invokeCallback(Status.OK_STATUS);
+ }
+ }
+ else {
+ invokeCallback(Status.OK_STATUS);
+ }
+ }
+ else {
+ String message = getErrorMessage(error);
+ IStatus status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), message, error);
+ invokeCallback(status);
+ }
+ }
+
+ /**
+ * Invoke the callback handler if it is not null using
+ * the specified status.
+ *
+ * @param status The refreshing result.
+ */
+ protected void invokeCallback(IStatus status) {
+ if(callback != null) {
+ callback.done(this, status);
+ }
+ }
+
+ /**
+ * Update the node's target digest and invoke the callback
+ * when the job is done.
+ */
+ private void updateTargetDigest() {
+ JobExecutor executor = new JobExecutor(callback);
+ executor.execute(new OpTargetFileDigest(node));
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFChannelException.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFChannelException.java
new file mode 100644
index 000000000..d75e79796
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFChannelException.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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.core.internal.exceptions;
+
+/**
+ * TCF channel exception.
+ */
+public class TCFChannelException extends TCFException {
+ private static final long serialVersionUID = 7414816212710485160L;
+
+ /**
+ * Constructor.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFChannelException(int severity, String message) {
+ super(severity, message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFChannelException(int severity, String message, Throwable cause){
+ super(severity, message, cause);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFException.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFException.java
new file mode 100644
index 000000000..e42569a5a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFException.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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.core.internal.exceptions;
+
+/**
+ * TCF file system implementation base exception.
+ */
+public class TCFException extends Exception {
+ private static final long serialVersionUID = -220092425137980661L;
+
+ // The severity code of this exception, could be used in job status handling.
+ private int severity;
+
+ /**
+ * Constructor.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFException(int severity, String message) {
+ super(message);
+ this.severity = severity;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFException(int severity, String message, Throwable cause) {
+ super(message, cause);
+ this.severity = severity;
+ }
+
+ /**
+ * Returns the severity. The severities are as follows (in
+ * descending order):
+ * <ul>
+ * <li><code>CANCEL</code> - cancelation occurred</li>
+ * <li><code>ERROR</code> - a serious error (most severe)</li>
+ * <li><code>WARNING</code> - a warning (less severe)</li>
+ * <li><code>INFO</code> - an informational ("fyi") message (least severe)</li>
+ * <li><code>OK</code> - everything is just fine</li>
+ * </ul>
+ * <p>
+ * The severity of a multi-status is defined to be the maximum
+ * severity of any of its children, or <code>OK</code> if it has
+ * no children.
+ * </p>
+ *
+ * @return the severity: one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @see #matches(int)
+ */
+ public int getSeverity() {
+ return severity;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFFileSystemException.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFFileSystemException.java
new file mode 100644
index 000000000..85f70b662
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/exceptions/TCFFileSystemException.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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.core.internal.exceptions;
+
+/**
+ * TCF remote file system exception.
+ */
+public class TCFFileSystemException extends TCFException {
+ private static final long serialVersionUID = -5203855887734608373L;
+
+ /**
+ * Constructor.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFFileSystemException(int severity, String message) {
+ super(severity, message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFFileSystemException(int severity, String message, Throwable cause) {
+ super(severity, message, cause);
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/IOpExecutor.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/IOpExecutor.java
new file mode 100644
index 000000000..f88fdb173
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/IOpExecutor.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
+
+/**
+ * The interface is to defined an operation executor, which executes
+ * the given operation providing the context including the progress monitor.
+ */
+public interface IOpExecutor {
+ /**
+ * Execute the specified operation providing an execution
+ * context.
+ *
+ * @param operation The operation to be executed.
+ * @return a result status of the execution.
+ */
+ public IStatus execute(IOperation operation);
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/JobExecutor.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/JobExecutor.java
new file mode 100644
index 000000000..a46e64e62
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/JobExecutor.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+
+/**
+ * The operation that is executed as a back ground job.
+ */
+public class JobExecutor implements IOpExecutor{
+ // The callback
+ protected ICallback callback;
+
+ /**
+ * Create an instance with no callback.
+ */
+ public JobExecutor() {
+ this(null);
+ }
+
+ /**
+ * Create an instance with the specified callback.
+ *
+ * @param callback called when the creation is done.
+ */
+ public JobExecutor(ICallback callback) {
+ this.callback = callback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.ui.internal.operations.IOpExecutor#execute(org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation)
+ */
+ @Override
+ public IStatus execute(final IOperation operation) {
+ Job job = new Job(operation.getName()){
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.setTaskName(operation.getName());
+ monitor.beginTask(operation.getName(), operation.getTotalWork());
+ operation.run(monitor);
+ return Status.OK_STATUS;
+ }
+ catch (InvocationTargetException e) {
+ Throwable throwable = e.getTargetException();
+ if(throwable instanceof TCFException) {
+ int severity = ((TCFException)throwable).getSeverity();
+ return new Status(severity, CorePlugin.getUniqueIdentifier(), throwable.getMessage(), throwable);
+ }
+ return new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), throwable.getMessage(), throwable);
+ }
+ catch (InterruptedException e) {
+ return Status.CANCEL_STATUS;
+ }
+ finally {
+ monitor.done();
+ }
+ }};
+ job.addJobChangeListener(new JobChangeAdapter(){
+ @Override
+ public void done(final IJobChangeEvent event) {
+ doCallback(operation, event);
+ }
+ });
+ job.schedule();
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Called when the creation is done.
+ *
+ * @param operation The operation object.
+ * @param event The job change event.
+ */
+ void doCallback(IOperation operation, IJobChangeEvent event) {
+ IStatus status = event.getResult();
+ if(callback != null) {
+ callback.done(operation, status);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/NullOpExecutor.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/NullOpExecutor.java
new file mode 100644
index 000000000..369b9063b
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/NullOpExecutor.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+
+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.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+
+/**
+ * An operation executor that executes an operation directly and silently.
+ */
+public class NullOpExecutor implements IOpExecutor {
+ // The callback being invoked after execution.
+ ICallback callback;
+
+ /**
+ * Empty argument constructor
+ */
+ public NullOpExecutor() {
+ this(null);
+ }
+
+ /**
+ * Create an instance with a callback.
+ *
+ * @param callback The callback to be invoked after execution.
+ */
+ public NullOpExecutor(ICallback callback) {
+ this.callback = callback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.IOpExecutor#execute(org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation)
+ */
+ @Override
+ public IStatus execute(IOperation operation) {
+ IProgressMonitor monitor = new NullProgressMonitor();
+ IStatus status;
+ try {
+ monitor.setTaskName(operation.getName());
+ monitor.beginTask(operation.getName(), operation.getTotalWork());
+ operation.run(monitor);
+ status = Status.OK_STATUS;
+ }
+ catch (InvocationTargetException e) {
+ Throwable throwable = e.getTargetException();
+ if (throwable instanceof TCFException) {
+ int severity = ((TCFException) throwable).getSeverity();
+ status = new Status(severity, CorePlugin.getUniqueIdentifier(), throwable.getMessage(), throwable);
+ }
+ else {
+ status = new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), throwable.getMessage(), throwable);
+ }
+ }
+ catch (InterruptedException e) {
+ status = Status.CANCEL_STATUS;
+ }
+ finally {
+ monitor.done();
+ }
+ if(callback != null) callback.done(operation, status);
+ return status;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheFileDigest.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheFileDigest.java
new file mode 100644
index 000000000..7aaca2c18
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheFileDigest.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The operation to calculate the message digest of a cache file.
+ */
+public class OpCacheFileDigest implements IOperation {
+ // The digest of which is going to be computed.
+ FSTreeNode node;
+ // The computing result
+ byte[] digest;
+
+ /**
+ * Create an operation to compute the digest of its local cache file.
+ *
+ * @param node The file system node.
+ */
+ public OpCacheFileDigest(FSTreeNode node) {
+ this.node = node;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ File file = CacheManager.getCacheFile(node);
+ BufferedInputStream input = null;
+ try {
+ long totalSize = file.length();
+ int chunk_size = (int) totalSize / 100;
+ int percentRead = 0;
+ long bytesRead = 0;
+ MessageDigest digest = MessageDigest.getInstance(MD_ALG);
+ input = new BufferedInputStream(new DigestInputStream(new FileInputStream(file), digest));
+ byte[] data = new byte[OpStreamOp.DEFAULT_CHUNK_SIZE];
+ int length;
+ while ((length = input.read(data)) >= 0){
+ bytesRead += length;
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ }
+ }
+ }
+ this.digest = digest.digest();
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new InvocationTargetException(e);
+ }
+ catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ finally {
+ if (input != null) {
+ try {input.close();} catch (Exception e) {}
+ }
+ }
+ }
+
+ /**
+ * Get the computing result.
+ *
+ * @return The message digest of this cache file.
+ */
+ public byte[] getDigest() {
+ return digest;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return "Update cache digest"; //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return 100;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheUpdate.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheUpdate.java
new file mode 100644
index 000000000..d9f668c6a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCacheUpdate.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.FileState;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The operation class that updates the local cache to target file systems.
+ */
+public class OpCacheUpdate extends OpDownload {
+
+ /**
+ * Create an instance of an OpCacheUpdate which
+ * updates the specified nodes.
+ *
+ * @param nodes The nodes to be updated.
+ */
+ public OpCacheUpdate(FSTreeNode... nodes) {
+ super(nodes);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpDownload#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ for (FSTreeNode node : srcNodes) {
+ // Write the data to its local cache file.
+ File file = CacheManager.getCachePath(node).toFile();
+ if (file.exists() && !file.canWrite()) {
+ // If the file exists and is read-only, delete it.
+ deleteFileChecked(file);
+ }
+ }
+ try {
+ super.run(monitor);
+ } finally {
+ if (!monitor.isCanceled()) {
+ for (FSTreeNode node : srcNodes) {
+ File file = CacheManager.getCachePath(node).toFile();
+ if (file.exists()) {
+ // If downloading is successful, update the attributes of the file and
+ // set the last modified time to that of its corresponding file.
+ if (!node.isWritable()) setReadOnlyChecked(file);
+ }
+ }
+ }
+ monitor.done();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpDownload#updateNodeDigest(org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode, byte[])
+ */
+ @Override
+ protected void updateNodeDigest(FSTreeNode node, byte[] digest) {
+ FileState fdigest = PersistenceManager.getInstance().getFileDigest(node);
+ fdigest.reset(digest);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpClipboard.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpClipboard.java
new file mode 100644
index 000000000..4b92a59b8
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpClipboard.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.beans.PropertyChangeEvent;
+import java.util.List;
+
+import org.eclipse.tcf.te.core.utils.PropertyChangeProvider;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The clip board to which copy or cut files/folders.
+ */
+public class OpClipboard extends PropertyChangeProvider {
+ // The constants to define the current operation type of the clip board.
+ private static final int NONE = -1;
+ private static final int CUT = 0;
+ private static final int COPY = 1;
+ // The operation type, CUT, COPY or NONE.
+ private int operation;
+ // The currently selected files/folders.
+ private List<FSTreeNode> files;
+
+ /**
+ * Create a clip board instance.
+ */
+ public OpClipboard() {
+ operation = NONE;
+ }
+
+ /**
+ * If the current operation is a cut operation.
+ *
+ * @return true if it is.
+ */
+ public boolean isCutOp() {
+ return operation == CUT;
+ }
+
+ /**
+ * If the current operation is a copy operation.
+ *
+ * @return true if it is.
+ */
+ public boolean isCopyOp() {
+ return operation == COPY;
+ }
+
+ /**
+ * If the clip board is empty.
+ *
+ * @return true if the operation is NONE and no files are selected.
+ */
+ public boolean isEmpty() {
+ return operation == NONE && (files == null || files.isEmpty());
+ }
+
+ /**
+ * Get the currently selected files/folders to operated.
+ *
+ * @return The file/folder list using their location URLs.
+ */
+ public List<FSTreeNode> getFiles() {
+ return files;
+ }
+
+ /**
+ * Cut the specified files/folders to the clip board.
+ *
+ * @param files The file/folder nodes.
+ */
+ public void cutFiles(List<FSTreeNode> files) {
+ operation = CUT;
+ this.files = files;
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "cut", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Copy the specified files/folders to the clip board.
+ *
+ * @param files The file/folder nodes.
+ */
+ public void copyFiles(List<FSTreeNode> files) {
+ operation = COPY;
+ this.files = files;
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "copy", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Clear the clip board.
+ */
+ public void clear() {
+ operation = NONE;
+ this.files = null;
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "clear", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Dispose the clipboard.
+ */
+ public void dispose() {
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCommitAttr.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCommitAttr.java
new file mode 100644
index 000000000..6e95f637f
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCommitAttr.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneSetStat;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+/**
+ * The operation implementation to commit the new attributes to
+ * the file system node.
+ */
+public class OpCommitAttr extends Operation {
+ // The node whose attributes are being updated.
+ FSTreeNode node;
+ // The new attributes for the file system node.
+ IFileSystem.FileAttrs attrs;
+
+ /**
+ * Create an instance
+ */
+ public OpCommitAttr(FSTreeNode node, IFileSystem.FileAttrs attrs) {
+ this.node = node;
+ this.attrs = attrs;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ IChannel channel = null;
+ try {
+ channel = openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = Operation.getBlockingFileSystem(channel);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ String path = node.getLocation(true);
+ service.setstat(path, attrs, new DoneSetStat() {
+ @Override
+ public void doneSetStat(IToken token, FileSystemException error) {
+ if (error == null) {
+ node.setAttributes(attrs);
+ } else {
+ errors[0] = newTCFException(IStatus.WARNING, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ } else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, node.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ catch(TCFException e) {
+ throw new InvocationTargetException(e);
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCopy.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCopy.java
new file mode 100644
index 000000000..eec8ce292
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCopy.java
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneCopy;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The operation class that copies selected FSTreeNodes to a specify destination folder.
+ */
+public class OpCopy extends Operation {
+ // The nodes to be copied.
+ List<FSTreeNode> nodes;
+ // The destination folder to be copied to.
+ FSTreeNode dest;
+ // The callback invoked to confirm overwriting when there're files with same names.
+ IConfirmCallback confirmCallback;
+ // If it is required to copy the permissions.
+ boolean cpPermission;
+ // If it is required to copy the ownership.
+ boolean cpOwnership;
+
+ /**
+ * Create a copy operation using the specified nodes and destination folder.
+ *
+ * @param nodes The file/folder nodes to be copied.
+ * @param dest The destination folder to be copied to.
+ */
+ public OpCopy(List<FSTreeNode> nodes, FSTreeNode dest) {
+ this(nodes, dest, false, false, null);
+ }
+
+ /**
+ * Create a copy operation using the specified nodes and destination folder,
+ * using the specified flags of copying permissions and ownership and a callback
+ * to confirm to overwrite existing files.
+ *
+ * @param nodes The file/folder nodes to be copied.
+ * @param dest The destination folder to be copied to.
+ */
+ public OpCopy(List<FSTreeNode> nodes, FSTreeNode dest, boolean cpPerm, boolean cpOwn, IConfirmCallback confirmCallback) {
+ super();
+ this.nodes = getAncestors(nodes);
+ this.dest = dest;
+ this.cpOwnership = cpOwn;
+ this.cpPermission = cpPerm;
+ this.confirmCallback = confirmCallback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ FSTreeNode head = nodes.get(0);
+ IChannel channel = null;
+ try {
+ channel = openChannel(head.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ for (FSTreeNode node : nodes) {
+ // Iterate the nodes and copy each of them to the destination folder.
+ copyNode(service, node, dest);
+ }
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, head.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e, e.getMessage());
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ monitor.done();
+ }
+ }
+
+ /**
+ * Copy the file/folder represented by the specified node to the destination folder.
+ *
+ * @param service The file system service to do the remote copying.
+ * @param node The file/folder node to be copied.
+ * @param dest The destination folder.
+ * @throws TCFFileSystemException The exception thrown during copying
+ * @throws InterruptedException The exception thrown when the operation is canceled.
+ */
+ void copyNode(IFileSystem service, FSTreeNode node, FSTreeNode dest) throws TCFFileSystemException, InterruptedException {
+ if (node.isFile()) {
+ copyFile(service, node, dest);
+ }
+ else if (node.isDirectory()) {
+ copyFolder(service, node, dest);
+ }
+ }
+
+ /**
+ * Copy the folder represented by the specified node to the destination folder.
+ *
+ * @param service The file system service to do the remote copying.
+ * @param node The folder node to be copied.
+ * @param dest The destination folder.
+ * @throws TCFFileSystemException The exception thrown during copying
+ * @throws InterruptedException The exception thrown when the operation is canceled.
+ */
+ private void copyFolder(IFileSystem service, FSTreeNode node, FSTreeNode dest) throws TCFFileSystemException, InterruptedException {
+ if (monitor.isCanceled()) throw new InterruptedException();
+ FSTreeNode copy = findChild(service, dest, node.name);
+ if (copy == null) {
+ // If no existing directory with the same name, create it.
+ copy = (FSTreeNode) node.clone();
+ addChild(service, dest, copy);
+ mkdir(service, copy);
+ copyChildren(service, node, copy);
+ }
+ else if (node.equals(copy)) {
+ copy = createCopyDestination(service, node, dest);
+ mkdir(service, copy);
+ copyChildren(service, node, copy);
+ }
+ else if (confirmReplace(node, confirmCallback)) {
+ copyChildren(service, node, copy);
+ }
+ monitor.worked(1);
+ }
+
+ /**
+ * Copy the children of the node to the destination folder.
+ *
+ * @param service The file system service to do the remote copying.
+ * @param node The folder node to be copied.
+ * @param dest The destination folder.
+ * @throws TCFFileSystemException The exception thrown during copying
+ * @throws InterruptedException The exception thrown when the operation is canceled.
+ */
+ private void copyChildren(IFileSystem service, FSTreeNode node, FSTreeNode dest) throws TCFFileSystemException, InterruptedException {
+ List<FSTreeNode> children = getChildren(node, service);
+ if (!children.isEmpty()) {
+ for (FSTreeNode child : children) {
+ // Iterate and copy its children nodes.
+ copyNode(service, child, dest);
+ }
+ }
+ }
+
+ /**
+ * Copy the file represented by the specified node to the destination folder.
+ *
+ * @param service The file system service to do the remote copying.
+ * @param node The file node to be copied.
+ * @param dest The destination folder.
+ * @throws TCFFileSystemException The exception thrown during copying
+ * @throws InterruptedException The exception thrown when the operation is canceled.
+ */
+ private void copyFile(IFileSystem service, FSTreeNode node, FSTreeNode dest) throws TCFFileSystemException, InterruptedException {
+ if (monitor.isCanceled()) throw new InterruptedException();
+ monitor.subTask(NLS.bind(Messages.OpCopy_Copying, node.name));
+ // Create the copy target file
+ final FSTreeNode copy = createCopyDestination(service, node, dest);
+ String src_path = node.getLocation(true);
+ String dst_path = copy.getLocation(true);
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ // Get the options of copy permission and ownership.
+ service.copy(src_path, dst_path, cpPermission, cpOwnership, new DoneCopy() {
+ @Override
+ public void doneCopy(IToken token, FileSystemException error) {
+ if (error != null) {
+ String message = NLS.bind(Messages.OpCopy_CannotCopyFile, copy.name, error);
+ errors[0] = new TCFFileSystemException(IStatus.ERROR, message, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ removeChild(service, dest, copy);
+ throw errors[0];
+ }
+ monitor.worked(1);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return Messages.OpCopy_CopyingFile;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ if(nodes != null && !nodes.isEmpty()) {
+ final AtomicReference<Integer> ref = new AtomicReference<Integer>();
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose.
+ }
+ @Override
+ public void run() throws Exception {
+ FSTreeNode head = nodes.get(0);
+ IChannel channel = null;
+ try {
+ channel = openChannel(head.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ ref.set(Integer.valueOf(count(service, nodes)));
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, head.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ }
+ }});
+ Integer value = ref.get();
+ return value == null ? IProgressMonitor.UNKNOWN : value.intValue();
+ }
+ return IProgressMonitor.UNKNOWN;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreate.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreate.java
new file mode 100644
index 000000000..ad9460869
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreate.java
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneStat;
+import org.eclipse.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The base operation class for creating a file or a folder in the file system of Target
+ * Explorer.
+ */
+public abstract class OpCreate extends Operation {
+ // The folder in which a file/folder is going to be created.
+ final protected FSTreeNode folder;
+ // The node that is created after the operation.
+ protected FSTreeNode node;
+ // The name of the node to be created.
+ final protected String name;
+
+ /**
+ * Create an FSCreate instance with the specified folder and the name of the new node.
+ *
+ * @param folder The folder in which the new node is going to be created. Must not be <code>null</code>.
+ * @param name The new node's name. Must not be <code>null</code>.
+ */
+ public OpCreate(FSTreeNode folder, String name) {
+ Assert.isNotNull(folder);
+ this.folder = folder;
+ Assert.isNotNull(name);
+ this.name = name;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ IChannel channel = null;
+ try {
+ channel = openChannel(folder.peerNode.getPeer());
+ monitor.worked(1);
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ if (!folder.childrenQueried) {
+ // If the children of folder is not queried, load it first.
+ loadChildren(folder, service);
+ monitor.worked(1);
+ }
+ monitor.worked(1);
+ create(service);
+ monitor.worked(1);
+ addNode(service);
+ monitor.worked(1);
+ refresh(service);
+ monitor.worked(1);
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, folder.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e, e.getMessage());
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ monitor.done();
+ }
+ }
+
+ /**
+ * Refresh new node's stat using the file system service.
+ *
+ * @param service The file system service.
+ * @throws TCFFileSystemException Thrown when refreshing the new node's stat.
+ */
+ void refresh(final IFileSystem service) throws TCFFileSystemException {
+ if (node != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ String path = node.getLocation(true);
+ service.stat(path, new DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) {
+ if (error == null) {
+ if (node != null) node.setAttributes(attrs);
+ }
+ else {
+ errors[0] = newTCFException(IStatus.WARNING, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }
+ }
+
+ /**
+ * Add the new node to the folder and its RuntimeModel.
+ *
+ * @param service The file system service to be used.
+ * @throws TCFFileSystemException Thrown when adding.
+ */
+ void addNode(final IFileSystem service) throws TCFFileSystemException {
+ if (Protocol.isDispatchThread()) {
+ node = newTreeNode();
+ folder.addChild(node);
+ }
+ else {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ addNode(service);
+ }
+ catch (TCFFileSystemException e) {
+ errors[0] = e;
+ }
+ }
+ });
+ if (errors[0] != null) throw errors[0];
+ }
+ }
+
+ /**
+ * Create the new node, either a directory node or a file node.
+ *
+ * @return The new node.
+ */
+ protected abstract FSTreeNode newTreeNode();
+
+ /**
+ * Create the node in the target system.
+ *
+ * @param service The file system service used to create the new node.
+ * @throws TCFFileSystemException Thrown when creating the node.
+ */
+ protected abstract void create(IFileSystem service) throws TCFFileSystemException;
+
+ /**
+ * Get the node that is created by this operation.
+ *
+ * @return the node created.
+ */
+ public FSTreeNode getNode() {
+ return node;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return NLS.bind(Messages.OpCreate_TaskName, name);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return folder.childrenQueried ? 5 : 6;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFile.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFile.java
new file mode 100644
index 000000000..44d9bdc98
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFile.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneClose;
+import org.eclipse.tcf.services.IFileSystem.DoneOpen;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.RuntimeModel;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The file operation class to create a file in the file system of Target Explorer.
+ */
+public class OpCreateFile extends OpCreate {
+
+ /**
+ * Create an instance to create a file with the name in the folder.
+ *
+ * @param folder The folder in which the file is to be created. Must not be <code>null</code>.
+ * @param name The new file's name. Must not be <code>null</code>.
+ */
+ public OpCreateFile(FSTreeNode folder, String name) {
+ super(folder, name);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpCreate#create(org.eclipse.tcf.services.IFileSystem)
+ */
+ @Override
+ protected void create(IFileSystem service) throws TCFFileSystemException {
+ String path = folder.getLocation(true);
+ if (!path.endsWith("/")) path += "/"; //$NON-NLS-1$ //$NON-NLS-2$
+ path += name;
+ final FileSystemException[] errors = new FileSystemException[1];
+ // Open the file.
+ final IFileHandle[] handles = new IFileHandle[1];
+ service.open(path, IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC, null, new DoneOpen() {
+ @Override
+ public void doneOpen(IToken token, FileSystemException error, IFileHandle hdl) {
+ errors[0] = error;
+ handles[0] = hdl;
+ }
+ });
+ if (errors[0] != null) {
+ TCFFileSystemException exception = new TCFFileSystemException(IStatus.ERROR, errors[0].toString());
+ exception.initCause(errors[0]);
+ throw exception;
+ }
+ if (handles[0] == null) {
+ throw new TCFFileSystemException(IStatus.ERROR, Messages.TcfURLConnection_NoFileHandleReturned);
+ }
+ service.close(handles[0], new DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpCreate#newTreeNode()
+ */
+ @Override
+ protected FSTreeNode newTreeNode() {
+ return RuntimeModel.createFileNode(name, folder);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFolder.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFolder.java
new file mode 100644
index 000000000..f2ff3a59b
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpCreateFolder.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneMkDir;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.RuntimeModel;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The file operation class to create a folder in the file system of Target Explorer.
+ */
+public class OpCreateFolder extends OpCreate {
+
+ /**
+ * Create an instance to create a folder with the name in the folder.
+ *
+ * @param folder The folder in which the new folder is to be created. Must not be <code>null</code>.
+ * @param name The name of the new folder. Must not be <code>null</code>.
+ */
+ public OpCreateFolder(FSTreeNode folder, String name) {
+ super(folder, name);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpCreate#create(org.eclipse.tcf.services.IFileSystem)
+ */
+ @Override
+ protected void create(IFileSystem service) throws TCFFileSystemException {
+ String path = folder.getLocation(true);
+ if (!path.endsWith("/")) path += "/"; //$NON-NLS-1$ //$NON-NLS-2$
+ path += name;
+ final FileSystemException[] errors = new FileSystemException[1];
+ service.mkdir(path, null, new DoneMkDir() {
+ @Override
+ public void doneMkDir(IToken token, FileSystemException error) {
+ if (error != null) {
+ errors[0] = error;
+ }
+ }
+ });
+ if (errors[0] != null) {
+ TCFFileSystemException exception = new TCFFileSystemException(IStatus.ERROR, errors[0].toString());
+ exception.initCause(errors[0]);
+ throw exception;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpCreate#newTreeNode()
+ */
+ @Override
+ protected FSTreeNode newTreeNode() {
+ FSTreeNode node = RuntimeModel.createFolderNode(name, folder);
+ // Newly created folder does not have any children. Mark it as queried.
+ node.queryDone();
+ return node;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDelete.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDelete.java
new file mode 100644
index 000000000..5dea3e520
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDelete.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * FSDelete deletes the selected FSTreeNode list.
+ */
+public class OpDelete extends Operation {
+ private static final int RETRY_TIMES = 3;
+ //The nodes to be deleted.
+ List<FSTreeNode> nodes;
+ //The callback invoked to confirm deleting read-only files.
+ IConfirmCallback confirmCallback;
+
+ /**
+ * Create a delete operation using the specified nodes.
+ *
+ * @param nodes The nodes to be deleted.
+ */
+ public OpDelete(List<FSTreeNode> nodes, IConfirmCallback confirmCallback) {
+ this.nodes = getAncestors(nodes);
+ this.confirmCallback = confirmCallback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ FSTreeNode head = nodes.get(0);
+ IChannel channel = null;
+ try {
+ channel = openChannel(head.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ for (FSTreeNode node : nodes) {
+ remove(node, service);
+ }
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, head.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e, e.getMessage());
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ monitor.done();
+ }
+ }
+
+ /**
+ * Delete the file/folder node using the file system service.
+ *
+ * @param monitor The monitor to report the progress.
+ * @param node The file/folder node to be deleted.
+ * @param service The file system service.
+ * @throws TCFFileSystemException The exception thrown during deleting.
+ * @throws InterruptedException Thrown when the operation is canceled.
+ */
+ void remove(FSTreeNode node, IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if (node.isFile()) {
+ removeFile(node, service);
+ }
+ else if (node.isDirectory()) {
+ removeFolder(node, service);
+ }
+ }
+
+ /**
+ * Delete the folder node and its children using the file system service.
+ *
+ * @param monitor The monitor to report the progress.
+ * @param node The folder node to be deleted.
+ * @param service The file system service.
+ * @throws TCFFileSystemException The exception thrown during deleting.
+ * @throws InterruptedException Thrown when the operation is canceled.
+ */
+ @Override
+ protected void removeFolder(final FSTreeNode node, IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ List<FSTreeNode> children = getChildren(node, service);
+ if (!children.isEmpty()) {
+ for (FSTreeNode child : children) {
+ // Delete each child node.
+ remove(child, service);
+ }
+ }
+ monitor.subTask(NLS.bind(Messages.OpDelete_RemovingFileFolder, node.name));
+ super.removeFolder(node, service);
+ monitor.worked(1);
+ }
+
+ /**
+ * Delete the file node using the file system service.
+ *
+ * @param node The file node to be deleted.
+ * @param service The file system service.
+ * @throws TCFFileSystemException The exception thrown during deleting.
+ * @throws InterruptedException Thrown when the operation is canceled.
+ */
+ protected void removeFile(final FSTreeNode node, IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if (monitor.isCanceled()) throw new InterruptedException();
+ monitor.subTask(NLS.bind(Messages.OpDelete_RemovingFileFolder, node.name));
+ // If the file is read only on windows or not writable on unix, then make it deletable.
+ if (confirmCallback != null && confirmCallback.requires(node)) {
+ if (!yes2All) {
+ int result = confirmCallback.confirms(node);
+ if (result == 1) {
+ yes2All = true;
+ }
+ else if (result == 2) {
+ monitor.worked(1);
+ return;
+ }
+ else if (result == 3) {
+ // Cancel the whole operation
+ monitor.setCanceled(true);
+ throw new InterruptedException();
+ }
+ }
+ IStatus status = mkWritable(node);
+ if (!status.isOK()) return;
+ }
+ super.removeFile(node, service);
+ monitor.worked(1);
+ }
+
+ /**
+ * Make the file/folder writable by changing its properties.
+ * It will try several times before return.
+ *
+ * @param node the file/folder node.
+ */
+ private IStatus mkWritable(FSTreeNode node) {
+ final FSTreeNode clone = (FSTreeNode) node.clone();
+ if (node.isWindowsNode()) {
+ clone.setReadOnly(false);
+ }
+ else {
+ clone.setWritable(true);
+ }
+ // Make the file writable.
+ OpCommitAttr op = new OpCommitAttr(node, clone.attr);
+ IOpExecutor executor = new NullOpExecutor();
+ IStatus status = null;
+ for (int i = 0; i < RETRY_TIMES; i++) {
+ status = executor.execute(op);
+ if (status.isOK()) return status;
+ }
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return Messages.OpDelete_Deleting;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ if(nodes != null && !nodes.isEmpty()) {
+ final AtomicReference<Integer> ref = new AtomicReference<Integer>();
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose.
+ }
+ @Override
+ public void run() throws Exception {
+ FSTreeNode head = nodes.get(0);
+ IChannel channel = null;
+ try {
+ channel = openChannel(head.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ ref.set(Integer.valueOf(count(service, nodes)));
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, head.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ }
+ }});
+ Integer value = ref.get();
+ return value == null ? IProgressMonitor.UNKNOWN : value.intValue();
+ }
+ return IProgressMonitor.UNKNOWN;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDownload.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDownload.java
new file mode 100644
index 000000000..3b72c9355
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpDownload.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLConnection;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * Download multiple files from local system to a remote system.
+ */
+public class OpDownload extends OpStreamOp {
+ // The destination files to be downloaded to.
+ protected File[] dstFiles;
+ // The source nodes to be downloaded from.
+ protected FSTreeNode[] srcNodes;
+
+ /**
+ * Create a download operation to download a file node
+ * to a local file.
+ *
+ * @param dstFile The local file to be downloaded to.
+ * @param srcNode The source node to be downloaded from.
+ */
+ public OpDownload(File dstFile, FSTreeNode srcNode) {
+ this(new File[]{dstFile}, new FSTreeNode[]{srcNode});
+ }
+
+ /**
+ * Create a download operation to download file nodes
+ * to local files.
+ *
+ * @param dstFiles The local files to be downloaded to.
+ * @param srcNodes The source nodes to be downloaded from.
+ */
+ public OpDownload(File[] dstFiles, FSTreeNode[] srcNodes) {
+ this.dstFiles = dstFiles;
+ this.srcNodes = srcNodes;
+ }
+
+ /**
+ * Create a download operation to download specified nodes
+ * to its local cache files.
+ *
+ * @param srcNodes The source file nodes to be downloaded.
+ */
+ public OpDownload(FSTreeNode... srcNodes) {
+ this.srcNodes = srcNodes;
+ this.dstFiles = new File[srcNodes.length];
+ for (int i = 0; i < srcNodes.length; i++) {
+ this.dstFiles[i] = CacheManager.getCacheFile(srcNodes[i]);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ try {
+ downloadFiles(dstFiles, srcNodes);
+ if(monitor.isCanceled()) throw new InterruptedException();
+ } catch (MalformedURLException e) {
+ throw new InvocationTargetException(e, e.getLocalizedMessage());
+ } catch (IOException e) {
+ throw new InvocationTargetException(e, e.getLocalizedMessage());
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Download the specified file list to the specified locations, reporting the progress
+ * using the specified monitor.
+ *
+ * @param dstFiles The file list to be downloaded to.
+ * @param srcNodes The node list to be downloaded from.
+ * @param monitor The monitor that reports progress.
+ * @throws IOException The exception reported during downloading.
+ */
+ private void downloadFiles(File[] dstFiles, FSTreeNode[] srcNodes) throws IOException {
+ // The buffer used to download the file.
+ byte[] data = new byte[DEFAULT_CHUNK_SIZE];
+ // Calculate the total size.
+ long totalSize = 0;
+ for (FSTreeNode node:srcNodes) {
+ totalSize += node.attr == null ? 0L : node.attr.size;
+ }
+ // Calculate the chunk size of one percent.
+ int chunk_size = (int) totalSize / 100;
+ // The current reading percentage.
+ int percentRead = 0;
+ // The current length of read bytes.
+ long bytesRead = 0;
+ for (int i = 0; i < srcNodes.length && !monitor.isCanceled(); i++) {
+ FSTreeNode node = srcNodes[i];
+ long size = node.attr == null ? 0L : node.attr.size;
+ MessageDigest digest = null;
+ BufferedInputStream input = null;
+ BufferedOutputStream output = null;
+ try {
+ URL url = node.getLocationURL();
+ TcfURLConnection connection = (TcfURLConnection) url.openConnection();
+ try {
+ digest = MessageDigest.getInstance(MD_ALG);
+ input = new BufferedInputStream(new DigestInputStream(connection.getInputStream(), digest));
+ }
+ catch (NoSuchAlgorithmException e) {
+ input = new BufferedInputStream(connection.getInputStream());
+ }
+ output = new BufferedOutputStream(new FileOutputStream(dstFiles[i]));
+
+ // Total size displayed on the progress dialog.
+ String fileLength = formatSize(size);
+ int length;
+ while ((length = input.read(data)) >= 0 && !monitor.isCanceled()) {
+ output.write(data, 0, length);
+ output.flush();
+ bytesRead += length;
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ // Report the progress.
+ monitor.subTask(NLS.bind(Messages.OpDownload_Downloading, new Object[]{node.name, formatSize(bytesRead), fileLength}));
+ }
+ }
+ }
+ }
+ finally {
+ if (output != null) {
+ try {
+ output.close();
+ } catch (Exception e) {
+ }
+ }
+ if (input != null) {
+ try {
+ input.close();
+ } catch (Exception e) {
+ }
+ }
+ if(digest != null) {
+ updateNodeDigest(node, digest.digest());
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the node's digest using the digest data.
+ *
+ * @param node The node whose digest should updated.
+ * @param digest The digest data.
+ */
+ protected void updateNodeDigest(FSTreeNode node, byte[] digest) {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ String message;
+ if(dstFiles.length==1)
+ message = NLS.bind(Messages.OpDownload_DownloadingSingleFile, dstFiles[0].getName());
+ else
+ message = NLS.bind(Messages.OpDownload_DownloadingMultipleFiles, Long.valueOf(dstFiles.length));
+ return message;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return 100;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpMove.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpMove.java
new file mode 100644
index 000000000..d1d0e50d1
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpMove.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneRename;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * FSMove moves specified tree nodes to a destination folder.
+ */
+public class OpMove extends Operation {
+ // The file/folder nodes to be moved.
+ List<FSTreeNode> nodes;
+ // The destination folder to be moved to.
+ FSTreeNode dest;
+ // The callback
+ IConfirmCallback confirmCallback;
+
+ /**
+ * Create a move operation to move the specified nodes to the destination folder.
+ *
+ * @param nodes The nodes to be moved.
+ * @param dest the destination folder to move to.
+ */
+ public OpMove(List<FSTreeNode> nodes, FSTreeNode dest) {
+ this(nodes, dest, null);
+ }
+
+ /**
+ * Create a move operation to move the specified nodes to the destination folder
+ * and a confirmation callback.
+ *
+ * @param nodes The nodes to be moved.
+ * @param dest the destination folder to move to.
+ * @param confirmCallback the confirmation callback.
+ */
+ public OpMove(List<FSTreeNode> nodes, FSTreeNode dest, IConfirmCallback confirmCallback) {
+ super();
+ this.nodes = getAncestors(nodes);
+ this.dest = dest;
+ this.confirmCallback = confirmCallback;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ // Remove its self from the clipped nodes.
+ nodes.remove(dest);
+ IChannel channel = null;
+ try {
+ if (!nodes.isEmpty()) {
+ FSTreeNode head = nodes.get(0);
+ channel = openChannel(head.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ for (FSTreeNode node : nodes) {
+ // Move each node.
+ moveNode(service, node, dest);
+ }
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, head.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e, e.getMessage());
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ monitor.done();
+ }
+ }
+
+ /**
+ * Move the file/folder to the destination folder using the specified file system service.
+ *
+ * @param monitor The monitor used to report the moving progress.
+ * @param service The file system service used to move the remote files.
+ * @param node The file/folder node to be moved.
+ * @param dest The destination folder.
+ * @throws TCFFileSystemException The exception thrown during moving.
+ * @throws InterruptedException Thrown when the operation is canceled.
+ */
+ void moveNode(IFileSystem service, final FSTreeNode node, FSTreeNode dest) throws TCFFileSystemException, InterruptedException {
+ if (monitor.isCanceled()) throw new InterruptedException();
+ monitor.subTask(NLS.bind(Messages.OpMove_Moving, node.name));
+ FSTreeNode copy = findChild(service, dest, node.name);
+ if (copy == null || !copy.equals(node) && confirmReplace(node, confirmCallback)) {
+ if (copy != null && copy.isDirectory() && node.isDirectory()) {
+ List<FSTreeNode> children = getChildren(node, service);
+ for (FSTreeNode child : children) {
+ moveNode(service, child, copy);
+ }
+ removeFolder(node, service);
+ monitor.worked(1);
+ }
+ else if (copy != null && copy.isFile() && node.isDirectory()) {
+ String error = NLS.bind(Messages.OpMove_FileExistsError, copy.name);
+ throw new TCFFileSystemException(IStatus.ERROR, error);
+ }
+ else if (copy != null && copy.isDirectory() && node.isFile()) {
+ String error = NLS.bind(Messages.OpMove_FolderExistsError, copy.name);
+ throw new TCFFileSystemException(IStatus.ERROR, error);
+ }
+ else {
+ if (copy != null && copy.isFile() && node.isFile()) {
+ removeFile(copy, service);
+ }
+ else if (copy == null) {
+ copy = (FSTreeNode) node.clone();
+ }
+ addChild(service, dest, copy);
+ String dst_path = copy.getLocation(true);
+ String src_path = node.getLocation(true);
+ final FSTreeNode copyNode = copy;
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ service.rename(src_path, dst_path, new DoneRename() {
+ @Override
+ public void doneRename(IToken token, FileSystemException error) {
+ if (error != null) {
+ String message = NLS.bind(Messages.OpMove_CannotMove, node.name, error);
+ errors[0] = new TCFFileSystemException(IStatus.ERROR, message, error);
+ }
+ else {
+ cleanUpNode(node, copyNode);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ removeChild(service, dest, copy);
+ throw errors[0];
+ }
+ monitor.worked(1);
+ }
+ }
+ }
+
+ /**
+ * Clean up the node after successful moving.
+ *
+ * @param node The node being moved.
+ * @param copyNode The target node that is moved to.
+ */
+ void cleanUpNode(FSTreeNode node, FSTreeNode copyNode) {
+ if (node.isFile()) {
+ super.cleanUpFile(node);
+ }
+ else if (node.isDirectory()) {
+ super.cleanUpFolder(node);
+ List<FSTreeNode> children = node.getChildren();
+ copyNode.addChidren(children);
+ for (FSTreeNode child : children) {
+ child.setParent(copyNode);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return Messages.OpMove_MovingFile;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return nodes == null ? IProgressMonitor.UNKNOWN : nodes.size();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpOutStreamOp.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpOutStreamOp.java
new file mode 100644
index 000000000..314feab03
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpOutStreamOp.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The operation class that download a file node to a specified output stream.
+ */
+public class OpOutStreamOp extends OpStreamOp {
+ // The output stream to write to.
+ private OutputStream output;
+ // The file node to download from.
+ private FSTreeNode node;
+
+ /**
+ * Create an operation instance to download the specified file to the specified
+ * output stream.
+ *
+ * @param node The file to be downloaded.
+ * @param output The output stream to write to.
+ */
+ public OpOutStreamOp(FSTreeNode node, OutputStream output) {
+ this.node = node;
+ this.output = output;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ InputStream input = null;
+ // Open the input stream of the node using the tcf stream protocol.
+ try {
+ URL url = node.getLocationURL();
+ InputStream in = url.openStream();
+ input = new BufferedInputStream(in);
+ // The buffer used to download the file.
+ byte[] data = new byte[DEFAULT_CHUNK_SIZE];
+ // Calculate the chunk size of one percent.
+ int chunk_size = (int) node.attr.size / 100;
+ // Total size displayed on the progress dialog.
+ String total_size = formatSize(node.attr.size);
+
+ int percentRead = 0;
+ long bytesRead = 0;
+ int length;
+ while ((length = input.read(data)) >= 0 && !monitor.isCanceled()) {
+ output.write(data, 0, length);
+ output.flush();
+ bytesRead += length;
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ // Report the progress.
+ monitor.subTask(NLS.bind(Messages.OpOutStreamOp_DownloadingProgress, formatSize(bytesRead), total_size));
+ }
+ }
+ }
+ if(monitor.isCanceled()) throw new InterruptedException();
+ }
+ catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (Exception e) {
+ }
+ }
+ monitor.done();
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpParsePath.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpParsePath.java
new file mode 100644
index 000000000..625f37747
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpParsePath.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.te.runtime.utils.Host;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.testers.TargetPropertyTester;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+import org.eclipse.tcf.te.tcf.locator.interfaces.services.IPeerModelLookupService;
+import org.eclipse.tcf.te.tcf.locator.model.ModelManager;
+
+/**
+ * The operation to parse a platform specific path to a target's node.
+ */
+public class OpParsePath extends Operation {
+ // The peer on which the file is located.
+ IPeerNode peer;
+ // The path on the target.
+ String path;
+ // The parsing result.
+ FSTreeNode result;
+
+ /**
+ * Create an instance with a path on a specified target.
+ *
+ * @param peer The target peer.
+ * @param path The path to be parsed.
+ */
+ public OpParsePath(IPeerNode peer, String path) {
+ this.peer = peer;
+ this.path = path;
+ }
+
+ /**
+ * The path of the cache file to be parsed.
+ *
+ * @param filePath The local cache's file.
+ */
+ public OpParsePath(String filePath) {
+ String cache_root = CacheManager.getCacheRoot().getAbsolutePath();
+ if (filePath.startsWith(cache_root)) {
+ filePath = filePath.substring(cache_root.length() + 1);
+ int slash = filePath.indexOf(File.separator);
+ if (slash != -1) {
+ String peerId = filePath.substring(0, slash);
+ peerId = peerId.replace(CacheManager.PATH_ESCAPE_CHAR, ':');
+
+ final AtomicReference<IPeerNode> peerNode = new AtomicReference<IPeerNode>();
+ final String finPeerId = peerId;
+
+ Runnable runnable = new Runnable() {
+
+ @Override
+ public void run() {
+ peerNode.set(ModelManager.getPeerModel().getService(IPeerModelLookupService.class).lkupPeerModelById(finPeerId));
+ }
+ };
+
+ if (Protocol.isDispatchThread()) runnable.run();
+ else Protocol.invokeAndWait(runnable);
+
+ this.peer = peerNode.get();
+ if (peer != null) {
+ boolean hostWindows = Host.isWindowsHost();
+ boolean windows = TargetPropertyTester.isWindows(peer);
+ filePath = filePath.substring(slash + 1);
+ if (hostWindows) {
+ if (windows) {
+ slash = filePath.indexOf(File.separator);
+ if (slash != -1) {
+ String disk = filePath.substring(0, slash);
+ filePath = filePath.substring(slash + 1);
+ disk = disk.replace(CacheManager.PATH_ESCAPE_CHAR, ':');
+ filePath = disk + File.separator + filePath;
+ }
+ }
+ else {
+ filePath = "/" + filePath.replace('\\', '/'); //$NON-NLS-1$
+ }
+ }
+ else {
+ if (windows) {
+ slash = filePath.indexOf(File.separator);
+ if (slash != -1) {
+ String disk = filePath.substring(0, slash);
+ filePath = filePath.substring(slash + 1);
+ disk = disk.replace(CacheManager.PATH_ESCAPE_CHAR, ':');
+ filePath = disk + File.separator + filePath;
+ }
+ filePath = filePath.replace(File.separatorChar, '\\');
+ }
+ else {
+ filePath = "/" + filePath; //$NON-NLS-1$
+ }
+ }
+ path = filePath;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the parsing result, which is a node that representing
+ * a file on the target system.
+ *
+ * @return The file system node.
+ */
+ public FSTreeNode getResult() {
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ if (peer != null && path != null) {
+ final FSTreeNode root = org.eclipse.tcf.te.tcf.filesystem.core.model.ModelManager.getRuntimeModel(peer).getRoot();
+ if (!root.childrenQueried) {
+ new NullOpExecutor().execute(new OpRefreshRoots(root));
+ }
+ Object[] elements = root.getChildren().toArray();
+ if (elements != null && elements.length != 0 && path.length() != 0) {
+ final FSTreeNode[] children = new FSTreeNode[elements.length];
+ System.arraycopy(elements, 0, children, 0, elements.length);
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable e) {
+ // Ignore exception
+ }
+
+ @Override
+ public void run() throws Exception {
+ result = findPath(peer.getPeer(), children, path);
+ }
+ });
+ }
+ }
+ }
+
+
+ /**
+ * Search the path in the children list. If it exists, then search the children of the found
+ * node recursively until the whole path is found. Or else return null.
+ *
+ * @param children The children nodes to search the path.
+ * @param path The path to be searched.
+ * @return The leaf node that has the searched path.
+ * @throws TCFException Thrown during searching.
+ */
+ FSTreeNode findPath(IPeer peer, FSTreeNode[] children, String path) throws TCFException, InterruptedException {
+ Assert.isTrue(children != null && children.length != 0);
+ Assert.isTrue(path != null && path.length() != 0);
+ FSTreeNode node = children[0];
+ String osPathSep = node.isWindowsNode() ? "\\" : "/"; //$NON-NLS-1$ //$NON-NLS-2$
+ int delim = path.indexOf(osPathSep);
+ String segment = null;
+ if (delim != -1) {
+ segment = path.substring(0, delim);
+ path = path.substring(delim + 1);
+ if (node.isRoot()) {
+ // If it is root directory, the name ends with the path separator.
+ segment += osPathSep;
+ }
+ }
+ else {
+ segment = path;
+ path = null;
+ }
+ node = findPathSeg(children, segment);
+ if (path == null || path.trim().length() == 0) {
+ // The end of the path.
+ return node;
+ }
+ else if (node != null) {
+ if (node.isDirectory()) {
+ List<FSTreeNode> nodes= new Operation().getChildren(node);
+ children = nodes.toArray(new FSTreeNode[nodes.size()]);
+ }
+ else {
+ children = null;
+ }
+ if (children != null && children.length != 0) {
+ return findPath(peer, children, path);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find in the children array the node that has the specified name.
+ *
+ * @param children The children array in which to find the node.
+ * @param name The name of the node to be searched.
+ * @return The node that has the specified name.
+ */
+ private FSTreeNode findPathSeg(FSTreeNode[] children, String name) {
+ for (FSTreeNode child : children) {
+ if (child.isWindowsNode()) {
+ if (child.name.equalsIgnoreCase(name)) return child;
+ }
+ else if (child.name.equals(name)) return child;
+ }
+ return null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefresh.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefresh.java
new file mode 100644
index 000000000..8df9e38eb
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefresh.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * FSRefresh refreshes a specified tree node and its children and grand children recursively.
+ */
+public class OpRefresh extends Operation {
+ //The root node to be refreshed.
+ FSTreeNode node;
+
+ /**
+ * Create an FSRefresh to refresh the specified node and its descendants.
+ *
+ * @param node The root node to be refreshed.
+ */
+ public OpRefresh(FSTreeNode node) {
+ this.node = node;
+ }
+
+ /**
+ * Create an FSRefresh to refresh the specified nodes and its descendants.
+ *
+ * @param nodes The node list to be refreshed.
+ */
+ public OpRefresh(List<FSTreeNode> nodes) {
+ this.node = getAncestor(nodes);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ if (node != null && (node.childrenQueried || node.isFile())) {
+ IChannel channel = null;
+ try {
+ channel = openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ refresh(node, service);
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, node.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e, e.getMessage());
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ monitor.done();
+ }
+ }
+ else {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Refresh the specified node and its children recursively using the file system service.
+ *
+ * @param node The node to be refreshed.
+ * @param service The file system service.
+ * @throws TCFFileSystemException Thrown during refreshing.
+ */
+ void refresh(final FSTreeNode node, final IFileSystem service) throws InterruptedException {
+ if(monitor.isCanceled()) throw new InterruptedException();
+ if ((node.isSystemRoot() || node.isDirectory()) && node.childrenQueried) {
+ if (!node.isSystemRoot()) {
+ try {
+ updateChildren(node, service);
+ }
+ catch (TCFException e) {
+ }
+ }
+ monitor.worked(1);
+ List<FSTreeNode> children = node.getChildren();
+ for (FSTreeNode child : children) {
+ refresh(child, service);
+ }
+ }
+ else if(node.isFile()) {
+ node.refresh();
+ }
+ }
+
+
+ /**
+ * Update the children of the specified folder node using the file system service.
+ *
+ * @param node The folder node.
+ * @param service The file system service.
+ * @throws TCFFileSystemException Thrown during querying the children nodes.
+ */
+ protected void updateChildren(final FSTreeNode node, final IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if(monitor.isCanceled()) throw new InterruptedException();
+ List<FSTreeNode> current = node.getChildren();
+ List<FSTreeNode> latest = queryChildren(node, service);
+ List<FSTreeNode> newNodes = diff(latest, current);
+ List<FSTreeNode> deleted = diff(current, latest);
+ node.removeChildren(deleted);
+ node.addChidren(newNodes);
+ }
+
+ /**
+ * Find those nodes which are in aList yet not in bList and return them as a list.
+ *
+ * @param aList
+ * @param bList
+ * @return the difference list.
+ */
+ private List<FSTreeNode> diff(List<FSTreeNode> aList, List<FSTreeNode> bList) {
+ List<FSTreeNode> newList = new ArrayList<FSTreeNode>();
+ for (FSTreeNode aNode : aList) {
+ boolean found = false;
+ for (FSTreeNode bNode : bList) {
+ if (aNode.name.equals(bNode.name)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ newList.add(aNode);
+ }
+ }
+ return newList;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return NLS.bind(Messages.OpRefresh_RefreshJobTitle, node == null ? "" : node.name); //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return count(node);
+ }
+
+ /**
+ * Count the nodes that should be refreshed under
+ * the specified directory.
+ *
+ * @param node The specified directory.
+ * @return the total count of the qualified nodes.
+ */
+ private int count(FSTreeNode node) {
+ if ((node.isSystemRoot() || node.isDirectory()) && node.childrenQueried) {
+ int total = 1;
+ List<FSTreeNode> children = node.getChildren();
+ for (FSTreeNode child : children) {
+ total += count(child);
+ }
+ return total;
+ }
+ return 0;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefreshRoots.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefreshRoots.java
new file mode 100644
index 000000000..6470c6e12
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRefreshRoots.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+/**
+ * The file operation class to create the root node in the file system of Target Explorer.
+ */
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DirEntry;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFChannelException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The operation to refresh the root of the file system.
+ */
+public class OpRefreshRoots extends Operation {
+ /* default */FSTreeNode root;
+
+ /**
+ * Create an instance using the peer model.
+ *
+ * @param peerModel The peer model.
+ */
+ public OpRefreshRoots(FSTreeNode root) {
+ this.root = root;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ IChannel channel = null;
+ try {
+ channel = openChannel(root.peerNode.getPeer());
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ root.queryStarted();
+ service.roots(new IFileSystem.DoneRoots() {
+ @Override
+ public void doneRoots(IToken token, FileSystemException error, DirEntry[] entries) {
+ if (error == null) {
+ for (DirEntry entry : entries) {
+ FSTreeNode node = new FSTreeNode(root, entry, true);
+ root.addChild(node);
+ }
+ }
+ }
+ });
+ // Reset the children query markers
+ root.queryDone();
+ }
+ }
+ catch(TCFChannelException e) {
+ throw new InvocationTargetException(e);
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRename.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRename.java
new file mode 100644
index 000000000..f7a0661c5
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpRename.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneRename;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+/**
+ * FSRename renames the specified file/folder to a
+ * new name.
+ *
+ */
+public class OpRename extends Operation {
+ // The file/folder node to be renamed.
+ FSTreeNode node;
+ // The new name the file/folder is renamed to.
+ String newName;
+
+ /**
+ * Create a rename operation that renames the node with the new name.
+ *
+ * @param node The file/folder node to be renamed.
+ * @param newName The new name of this node.
+ */
+ public OpRename(FSTreeNode node, String newName) {
+ this.node = node;
+ this.newName = newName;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ IChannel channel = null;
+ try {
+ channel = openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ renameNode(service);
+ }
+ else {
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, node.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e, e.getMessage());
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ monitor.done();
+ }
+ }
+
+ /**
+ * Rename the node using the new name.
+ *
+ * @param service File system service used to rename.
+ * @throws TCFFileSystemException The exception thrown during renaming.
+ */
+ void renameNode(IFileSystem service) throws TCFFileSystemException {
+ String src_path = node.getLocation(true);
+ String oldName = node.name;
+ node.name = newName;
+ String dst_path = node.getLocation(true);
+ node.name = oldName;
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ service.rename(src_path, dst_path, new DoneRename() {
+ @Override
+ public void doneRename(IToken token, FileSystemException error) {
+ if (error != null) {
+ String message = NLS.bind(Messages.OpRename_CannotRename, node.name, error);
+ errors[0] = new TCFFileSystemException(IStatus.ERROR, message, error);
+ }
+ else {
+ final File file = CacheManager.getCacheFile(node);
+ if (node.isFile() && file.exists()) {
+ PersistenceManager.getInstance().removeFileDigest(node.getLocationURI());
+ }
+ deleteFileChecked(file);
+ node.setName(newName);
+ }
+ }
+ });
+ monitor.worked(1);
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return Messages.OpRename_TitleRename;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpStreamOp.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpStreamOp.java
new file mode 100644
index 000000000..c2768a6e1
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpStreamOp.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.File;
+import java.text.DecimalFormat;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The operation class that streams file upward downward. It is the base
+ * class for uploading, downloading, cache update and commit.
+ */
+public abstract class OpStreamOp extends Operation {
+ // The formatter used to format the size displayed while downloading.
+ protected static final DecimalFormat SIZE_FORMAT = new DecimalFormat("#,##0.##"); //$NON-NLS-1$
+ // The default chunk size of the buffer used during downloading files.
+ public static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
+
+ /**
+ * Check if the file exists and set its read-only attribute if it does. Record
+ * the failure message if it fails.
+ *
+ * @param file The file to be set.
+ */
+ protected void setReadOnlyChecked(final File file) {
+ if (file.exists()) {
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void run() throws Exception {
+ if (!file.setReadOnly()) {
+ throw new Exception(NLS.bind(Messages.OpStreamOp_SetReadOnlyFailed, file.getAbsolutePath()));
+ }
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }});
+ }
+ }
+
+ /**
+ * Use the SIZE_FORMAT to format the file's size. The rule is: 1. If the
+ * size is less than 1024 bytes, then show it as "####" bytes. 2. If the
+ * size is less than 1024 KBs, while more than 1 KB, then show it as
+ * "####.##" KBs. 3. If the size is more than 1 MB, then show it as
+ * "####.##" MBs.
+ *
+ * @param size
+ * The file size to be displayed.
+ * @return The string representation of the size.
+ */
+ protected String formatSize(long size) {
+ double kbSize = size / 1024.0;
+ if (kbSize < 1.0) {
+ return SIZE_FORMAT.format(size) + Messages.OpStreamOp_Bytes;
+ }
+ double mbSize = kbSize / 1024.0;
+ if (mbSize < 1.0)
+ return SIZE_FORMAT.format(kbSize) + Messages.OpStreamOp_KBs;
+ return SIZE_FORMAT.format(mbSize) + Messages.OpStreamOp_MBs;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpTargetFileDigest.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpTargetFileDigest.java
new file mode 100644
index 000000000..328acbd3a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpTargetFileDigest.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The operation that computes the digest of the cache file in the background.
+ */
+public class OpTargetFileDigest implements IOperation {
+ // The digest of which is going to be computed.
+ FSTreeNode node;
+ // The computing result
+ byte[] digest;
+
+ /**
+ * Create an operation to compute the digest of its target file.
+ *
+ * @param node The file system node.
+ */
+ public OpTargetFileDigest(FSTreeNode node) {
+ this.node = node;
+ }
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ BufferedInputStream input = null;
+ try {
+ long totalSize = node.attr.size;
+ int chunk_size = (int) totalSize / 100;
+ int percentRead = 0;
+ long bytesRead = 0;
+ URL url = node.getLocationURL();
+ MessageDigest digest = MessageDigest.getInstance(MD_ALG);
+ input = new BufferedInputStream(new DigestInputStream(url.openStream(), digest));
+ // The buffer used to download the file.
+ byte[] data = new byte[OpStreamOp.DEFAULT_CHUNK_SIZE];
+ int length;
+ while ((length = input.read(data)) >= 0){
+ bytesRead += length;
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ }
+ }
+ }
+ this.digest = digest.digest();
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new InvocationTargetException(e);
+ }
+ catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ finally {
+ if (input != null) {
+ try {input.close();} catch (Exception e) {}
+ }
+ }
+ }
+
+ /**
+ * Get the computing result.
+ *
+ * @return The message digest of this cache file.
+ */
+ public byte[] getDigest() {
+ return digest;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return "Update target digest"; //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return 100;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java
new file mode 100644
index 000000000..54e20fcd9
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java
@@ -0,0 +1,431 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLConnection;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.FileState;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * Upload multiple files from local system to a remote system.
+ */
+public class OpUpload extends OpStreamOp {
+ // The source files to be uploaded.
+ File[] srcFiles;
+ // The destination URLs to be uploaded to.
+ URL[] dstURLs;
+ // The confirm callback
+ IConfirmCallback confirmCallback;
+ // The parent folder map to search files that have same names.
+ Map<File, FSTreeNode> parentFolders;
+ // The files that are to be committed to its target file system.
+ FSTreeNode[] nodes;
+
+ /**
+ * Constructor.
+ *
+ * @param srcFile The source file to be uploaded.
+ * @param dstURL The destination URL.
+ */
+ public OpUpload(File srcFile, URL dstURL) {
+ this(new File[]{srcFile}, new URL[]{dstURL});
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param srcFiles The source files to be uploaded.
+ * @param dstURLs The destination URLs.
+ */
+ public OpUpload(File[] srcFiles, URL[] dstURLs) {
+ this(srcFiles, dstURLs, null);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param sourceFiles The source files in the native file system to be uploaded.
+ * @param targetFolder The taret parent folder to upload these files to.
+ */
+ public OpUpload(String[]sourceFiles, FSTreeNode targetFolder) {
+ this(sourceFiles, targetFolder, null);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param sourceFiles The source files in the native file system to be uploaded.
+ * @param targetFolder The target parent folder to upload these files to.
+ * @param confirmCallback the confirmation callback to confirm overwriting.
+ */
+ public OpUpload(File[] srcFiles, URL[] dstURLs, IConfirmCallback confirmCallback) {
+ this.srcFiles = srcFiles;
+ this.dstURLs = dstURLs;
+ this.confirmCallback = confirmCallback;
+ }
+
+ /**
+ * Constructor that upload the local cache files of the specified nodes.
+ *
+ * @param nodes The nodes to be uploaded.
+ */
+ public OpUpload(FSTreeNode... nodes) {
+ srcFiles = new File[nodes.length];
+ dstURLs = new URL[nodes.length];
+ for (int i = 0; i < nodes.length; i++) {
+ srcFiles[i] = CacheManager.getCacheFile(nodes[i]);
+ dstURLs[i] = nodes[i].getLocationURL();
+ }
+ this.nodes = nodes;
+ }
+
+ /**
+ * Create an instance with specified files, target folder and a callback.
+ *
+ * @param sourceFiles the source files being uploaded.
+ * @param targetFolder the target folder to upload the files to.
+ * @param callback the callback that is invoked after uploading.
+ */
+ public OpUpload(String[] sourceFiles, FSTreeNode targetFolder, IConfirmCallback confirmCallback) {
+ this.confirmCallback = confirmCallback;
+ List<File> fileList = new ArrayList<File>();
+ List<URL> urlList = new ArrayList<URL>();
+ prepareDirStruct(sourceFiles, fileList, urlList, targetFolder);
+ srcFiles = fileList.toArray(new File[fileList.size()]);
+ dstURLs = urlList.toArray(new URL[urlList.size()]);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.internal.operations.FSOperation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ try {
+ uploadFiles(srcFiles, dstURLs);
+ if(monitor.isCanceled()) throw new InterruptedException();
+ } catch (MalformedURLException e) {
+ throw new InvocationTargetException(e);
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ /**
+ * Test if the specified file should be confirmed.
+ *
+ * @param file The file to be confirmed.
+ * @return true if it is.
+ */
+ private boolean requireConfirmation(File file) {
+ return parentFolders != null && confirmCallback != null && !yes2All && confirmCallback.requires(file) && findNode(file) != null;
+ }
+
+ /**
+ * Upload the specified file list to the specified locations, reporting the progress
+ * using the specified monitor.
+ *
+ * @param files The file list to be uploaded.
+ * @param urls The
+ * @param monitor
+ * @throws IOException
+ */
+ private void uploadFiles(File[] files, URL[] urls) throws IOException {
+ // The buffer used to download the file.
+ byte[] data = new byte[DEFAULT_CHUNK_SIZE];
+ // Calculate the total size.
+ long totalSize = 0;
+ for (File file:files) {
+ totalSize += file.length();
+ }
+ // Calculate the chunk size of one percent.
+ int chunk_size = (int) totalSize / 100;
+ // The current reading percentage.
+ int percentRead = 0;
+ // The current length of read bytes.
+ long bytesRead = 0;
+ for (int i = 0; i < files.length && !monitor.isCanceled(); i++) {
+ File file = files[i];
+ if (requireConfirmation(file)) {
+ int result = confirmCallback.confirms(file);
+ switch (result) {
+ case IConfirmCallback.YES:
+ break;
+ case IConfirmCallback.YES_TO_ALL:
+ yes2All = true;
+ break;
+ case IConfirmCallback.NO:
+ bytesRead += file.length();
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ // Report the progress.
+ monitor.subTask(NLS
+ .bind(Messages.OpUpload_UploadingProgress, new Object[] { file
+ .getName(), formatSize(bytesRead), formatSize(file
+ .length()) }));
+ }
+ }
+ continue;
+ case IConfirmCallback.CANCEL:
+ monitor.setCanceled(true);
+ continue;
+ }
+ }
+ BufferedInputStream input = null;
+ BufferedOutputStream output = null;
+ MessageDigest digest = null;
+ try {
+ URL url = urls[i];
+ TcfURLConnection connection = (TcfURLConnection) url.openConnection();
+ connection.setDoInput(false);
+ connection.setDoOutput(true);
+ if (nodes != null) {
+ try {
+ digest = MessageDigest.getInstance(MD_ALG);
+ input = new BufferedInputStream(new DigestInputStream(new FileInputStream(file), digest));
+ }
+ catch (NoSuchAlgorithmException e) {
+ input = new BufferedInputStream(new FileInputStream(file));
+ }
+ }
+ else {
+ input = new BufferedInputStream(new FileInputStream(file));
+ }
+ output = new BufferedOutputStream(connection.getOutputStream());
+
+ // Total size displayed on the progress dialog.
+ String fileLength = formatSize(file.length());
+ int length;
+ while ((length = input.read(data)) >= 0 && !monitor.isCanceled()) {
+ output.write(data, 0, length);
+ output.flush();
+ bytesRead += length;
+ if (chunk_size != 0) {
+ int percent = (int) bytesRead / chunk_size;
+ if (percent != percentRead) { // Update the progress.
+ monitor.worked(percent - percentRead);
+ percentRead = percent; // Remember the percentage.
+ // Report the progress.
+ monitor.subTask(NLS.bind(Messages.OpUpload_UploadingProgress, new Object[]{file.getName(), formatSize(bytesRead), fileLength}));
+ }
+ }
+ }
+ } finally {
+ if (output != null) {
+ try {
+ output.close();
+ } catch (Exception e) {
+ }
+ }
+ if (input != null) {
+ try {
+ input.close();
+ } catch (Exception e) {
+ }
+ }
+ if(digest != null && nodes != null) {
+ FileState filedigest = PersistenceManager.getInstance().getFileDigest(nodes[i]);
+ filedigest.reset(digest.digest());
+ }
+ }
+ }
+ }
+
+ /**
+ * Prepare the directory structure on the remote target, creating necessary intermediate directories
+ * and found all files that should be uploaded. The resulting files to be uploaded should be stored
+ * to the file list. The resulting corresponding target file URLs should be stored in the url list.
+ *
+ * @param fileList The file list to store the files that should be uploaded.
+ * @param urlList The list containing the corresponding urls.
+ */
+ private void prepareDirStruct(String[] sourceFiles, List<File> fileList, List<URL> urlList, FSTreeNode targetFolder) {
+ parentFolders = Collections.synchronizedMap(new HashMap<File, FSTreeNode>());
+ List<File> files = new ArrayList<File>();
+ for(String path: sourceFiles) {
+ files.add(new File(path));
+ }
+ // Find the root nodes of these files.
+ List<File> topFiles = getTopFiles(files);
+ for(File topFile : topFiles) {
+ appendFile(topFile, fileList, urlList, targetFolder);
+ }
+ }
+
+ /**
+ * Append the specified file object to the file list and url list. If the file object is a file
+ * then append it to the two lists. If the file object is a directory, then recursively
+ * add its children and grand children to the two list. During this process, the parents of
+ * these files and directories traversed should be put into the parent folders map so that
+ * it could be queried to check if it has a file/directory with a same name.
+ *
+ * @param file The file to be added
+ * @param fileList The file list
+ * @param urlList The url list
+ * @param parent The current parent node
+ */
+ private void appendFile(final File file, final List<File> fileList, final List<URL> urlList, final FSTreeNode parent) {
+ parentFolders.put(file, parent);
+ if(file.isFile()) {
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void run() throws Exception {
+ URL folderURL = parent.getLocationURL();
+ URL url = new URL(folderURL, file.getName());
+ fileList.add(file);
+ urlList.add(url);
+ }
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }});
+ } else if(file.isDirectory()) {
+ FSTreeNode node = findNode(file);
+ if(node == null) {
+ OpCreateFolder create = new OpCreateFolder(parent, file.getName());
+ new NullOpExecutor().execute(create);
+ node = create.getNode();
+ }
+ File[] children = file.listFiles();
+ for(File child : children) {
+ appendFile(child, fileList, urlList, node);
+ }
+ }
+ }
+
+ /**
+ * Get the root files of the specified files/folders in the list.
+ *
+ * @param files The files to be checked.
+ * @return Root nodes of these files that has no parent.
+ */
+ private List<File> getTopFiles(List<File>files) {
+ List<File> result = new ArrayList<File>();
+ for(File file : files) {
+ if(!hasFileAncestor(file, files)) {
+ result.add(file);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Check if the target file has an ancestral parent in the specified list.
+ *
+ * @param target The target file to be checked.
+ * @param files The file list to be searched.
+ * @return true if it has an ancestral parent.
+ */
+ private boolean hasFileAncestor(File target, List<File> files) {
+ for(File file : files) {
+ if(isFileAncestor(file, target)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if the specified "file" is an ancestral parent of the "target" file.
+ *
+ * @param file The ancestral file.
+ * @param target The target file.
+ * @return true if "file" is an ancestral parent of "target"
+ */
+ private boolean isFileAncestor(File file, File target) {
+ if(target == null) return false;
+ File parent = target.getParentFile();
+ if(file.equals(parent)) return true;
+ return isFileAncestor(file, parent);
+ }
+
+ /**
+ * Check if the specified file has a same-named file under its corresponding
+ * parent folder.
+ *
+ * @param file The file to checked.
+ * @return the node that has the same name with the file.
+ */
+ private FSTreeNode findNode(File file) {
+ final FSTreeNode parent = parentFolders.get(file);
+ if (parent != null) {
+ final List<FSTreeNode> targetChildren = new ArrayList<FSTreeNode>();
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ targetChildren.addAll(getChildren(parent));
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ });
+ String name = file.getName();
+ for (FSTreeNode child : targetChildren) {
+ if (name.equals(child.name)) return child;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ String message;
+ if(srcFiles.length==1)
+ message = NLS.bind(Messages.OpUpload_UploadSingleFile, srcFiles[0].getName());
+ else
+ message = NLS.bind(Messages.OpUpload_UploadNFiles, Long.valueOf(srcFiles.length));
+ return message;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return 100;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUser.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUser.java
new file mode 100644
index 000000000..269f9b142
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUser.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneUser;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.UserAccount;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+
+/**
+ * The operation to get the user account of a specified peer.
+ */
+public class OpUser extends Operation {
+ // The key to save and retrieve the user account in a peer model.
+ /* default */ static final String USER_ACCOUNT_KEY = CorePlugin.getUniqueIdentifier()+".user.account"; //$NON-NLS-1$
+
+ // The target peer.
+ IPeerNode peerNode;
+ // The resulting account.
+ UserAccount result;
+
+ /**
+ * Create an operation using the target peer.
+ * @param peerNode The peer whose user account to be checked.
+ */
+ public OpUser(IPeerNode peerNode) {
+ this.peerNode = peerNode;
+ }
+
+ /**
+ * Get the checking result.
+ *
+ * @return The user account checked.
+ */
+ public UserAccount getUserAccount() {
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ if(peerNode != null) {
+ result = getUserFromPeer(peerNode);
+ if (result == null) {
+ final UserAccount[] accounts = new UserAccount[1];
+ IChannel channel = null;
+ try {
+ channel = Operation.openChannel(peerNode.getPeer());
+ if (channel != null) {
+ accounts[0] = getUserByChannel(channel);
+ if (accounts[0] != null) setUserToPeer(peerNode, accounts[0]);
+ }
+ }
+ catch (TCFException e) {
+ throw new InvocationTargetException(e);
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ }
+ result = accounts[0];
+ }
+ }
+ }
+
+ /**
+ * Get the user account from the peer using the channel connected to the
+ * remote target.
+ *
+ * @param channel
+ * The channel connected to the remote target.
+ * @return The user account information or null if it fails.
+ */
+ UserAccount getUserByChannel(final IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = Operation.getBlockingFileSystem(channel);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final UserAccount[] accounts = new UserAccount[1];
+ service.user(new DoneUser() {
+ @Override
+ public void doneUser(IToken token, FileSystemException error, int real_uid, int effective_uid, int real_gid, int effective_gid, String home) {
+ if (error == null) {
+ accounts[0] = new UserAccount(real_uid, real_gid, effective_uid, effective_gid, home);
+ }else {
+ errors[0] = newTCFException(IStatus.WARNING, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ return accounts[0];
+ }
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, channel.getRemotePeer().getID());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+
+ /**
+ * Get the user account stored in the specified peer model using a key named
+ * "user.account" defined by the constant USER_ACCOUNT_KEY.
+ *
+ * @param peer
+ * The peer model from which the user account is retrieved.
+ * @return The user account if it exists or null if not.
+ */
+ private UserAccount getUserFromPeer(final IPeerNode peer) {
+ Assert.isNotNull(peer);
+ if (Protocol.isDispatchThread()) {
+ return (UserAccount) peer.getProperty(USER_ACCOUNT_KEY);
+ }
+ final UserAccount[] accounts = new UserAccount[1];
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ accounts[0] = (UserAccount) peer.getProperty(USER_ACCOUNT_KEY);
+ }
+ });
+ return accounts[0];
+ }
+
+ /**
+ * Save the user account to the specified peer model using a key named
+ * "user.account" defined by the constant USER_ACCOUNT_KEY.
+ *
+ * @param peer
+ * The peer model to which the user account is saved.
+ */
+ void setUserToPeer(final IPeerNode peer, final UserAccount account) {
+ Assert.isNotNull(peer);
+ Assert.isNotNull(account);
+
+ if (Protocol.isDispatchThread()) {
+ peer.setProperty(USER_ACCOUNT_KEY, account);
+ } else {
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ peer.setProperty(USER_ACCOUNT_KEY, account);
+ }
+ });
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/Operation.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/Operation.java
new file mode 100644
index 000000000..117667b3e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/Operation.java
@@ -0,0 +1,618 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IErrorReport;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DirEntry;
+import org.eclipse.tcf.services.IFileSystem.DoneMkDir;
+import org.eclipse.tcf.services.IFileSystem.DoneOpen;
+import org.eclipse.tcf.services.IFileSystem.DoneReadDir;
+import org.eclipse.tcf.services.IFileSystem.DoneRemove;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tcf.te.core.utils.Ancestor;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.core.concurrent.Rendezvous;
+import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFChannelException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.BlockingFileSystemProxy;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * Operation is the base class of file system operation classes.
+ * @see IOperation
+ */
+public class Operation extends Ancestor<FSTreeNode> implements IOperation {
+ // The default timeout waiting for blocked invocations.
+ public static final long DEFAULT_TIMEOUT = 60000L;
+ // The flag indicating if the following action should be executed without asking.
+ protected boolean yes2All = false;
+
+ // The current progress monitor, probably null.
+ protected IProgressMonitor monitor = new NullProgressMonitor();
+
+ /**
+ * Create an instance.
+ */
+ public Operation() {
+ }
+
+ /**
+ * Create a TCFFileSystemException from a FileSystemException.
+ *
+ * @param error The FileSystemException
+ * @return a TCFFileSystemException
+ */
+ protected TCFFileSystemException newTCFException(int severity, FileSystemException error) {
+ String message = null;
+ if(error instanceof IErrorReport) {
+ IErrorReport report = (IErrorReport) error;
+ message = (String)report.getAttributes().get(IErrorReport.ERROR_FORMAT);
+ }
+ return new TCFFileSystemException(severity, message, error);
+ }
+
+ /**
+ * Clean up the folder node after moving, deleting or copying.
+ *
+ * @param node the folder node that is to be cleaned.
+ */
+ protected void cleanUpFolder(FSTreeNode node) {
+ File file = CacheManager.getCacheFile(node);
+ deleteFileChecked(file);
+ FSTreeNode parent = node.getParent();
+ if (parent != null) {
+ parent.removeChild(node);
+ }
+ }
+
+ /**
+ * Check if the file exists and delete if it does. Record
+ * the failure message if deleting fails.
+ *
+ * @param file The file to be deleted.
+ */
+ protected void deleteFileChecked(final File file) {
+ if (file.exists()) {
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void run() throws Exception {
+ if (!file.delete()) {
+ throw new Exception(NLS.bind(Messages.Operation_DeletingFileFailed, file.getAbsolutePath()));
+ }
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }});
+ }
+ }
+
+ /**
+ * Clean up the file node after moving, deleting or copying.
+ *
+ * @param node the file node that is to be cleaned.
+ */
+ protected void cleanUpFile(FSTreeNode node) {
+ final File file = CacheManager.getCacheFile(node);
+ deleteFileChecked(file);
+ PersistenceManager.getInstance().removeFileDigest(node.getLocationURI());
+ FSTreeNode parent = node.getParent();
+ if (parent != null) {
+ parent.removeChild(node);
+ }
+ }
+
+ /**
+ * Open a channel connected to the target represented by the peer.
+ *
+ * @return The channel or null if the operation fails.
+ */
+ public static IChannel openChannel(final IPeer peer) throws TCFChannelException {
+ final TCFChannelException[] errors = new TCFChannelException[1];
+ final IChannel[] channels = new IChannel[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ Tcf.getChannelManager().openChannel(peer, null, new DoneOpenChannel() {
+ @Override
+ public void doneOpenChannel(Throwable error, IChannel channel) {
+ if (error != null) {
+ if (error instanceof ConnectException) {
+ String message = NLS.bind(Messages.Operation_NotResponding, peer.getID());
+ errors[0] = new TCFChannelException(IStatus.ERROR, message);
+ }
+ else if(!(error instanceof OperationCanceledException)) {
+ String message = NLS.bind(Messages.Operation_OpeningChannelFailureMessage, peer.getID(), error.getMessage());
+ errors[0] = new TCFChannelException(IStatus.OK, message, error);
+ }
+ }
+ else {
+ channels[0] = channel;
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch(TimeoutException e) {
+ throw new TCFChannelException(IStatus.ERROR, Messages.Operation_TimeoutOpeningChannel);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ return channels[0];
+ }
+
+ /**
+ * Get a blocking file system service from the channel. The
+ * returned file system service is a service that delegates the
+ * method call to the file system proxy. If the method returns
+ * asynchronously with a callback, it will block the call until
+ * the callback returns.
+ * <p>
+ * <em>Note: All the method of the returned file system
+ * service must be called outside of the dispatching thread.</em>
+ *
+ * @param channel The channel to get the file system service.
+ * @return The blocking file system service.
+ */
+ public static IFileSystem getBlockingFileSystem(final IChannel channel) {
+ if(Protocol.isDispatchThread()) {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ return new BlockingFileSystemProxy(service);
+ }
+ final IFileSystem[] service = new IFileSystem[1];
+ Protocol.invokeAndWait(new Runnable(){
+ @Override
+ public void run() {
+ service[0] = getBlockingFileSystem(channel);
+ }});
+ return service[0];
+ }
+
+ /**
+ * Count the total nodes in the node list including their children and grand children
+ * recursively.
+ *
+ * @param service The file system service used to open those folders that are not expanded yet.
+ * @param nodes The node list to be counted.
+ * @return The count of the total nodes.
+ * @throws TCFFileSystemException Thrown when expanding the unexpanded folders.
+ */
+ protected int count(IFileSystem service, List<FSTreeNode> nodes) throws TCFFileSystemException, InterruptedException {
+ int count = 0;
+ for (FSTreeNode node : nodes) {
+ if (node.isFile()) {
+ count++;
+ }
+ else if (node.isDirectory()) {
+ List<FSTreeNode> children = getChildren(node, service);
+ count += count(service, children) + 1;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the children of the specified folder node. If the folder node is not expanded, then
+ * expanded using the specified file system service.
+ *
+ * @param node The folder node.
+ * @param service The file system service.
+ * @return The children of the folder node.
+ * @throws TCFFileSystemException Thrown during querying the children nodes.
+ */
+ protected List<FSTreeNode> getChildren(final FSTreeNode node, final IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if (node.childrenQueried) {
+ return node.getChildren();
+ }
+ loadChildren(node, service);
+ return getChildren(node, service);
+ }
+
+ /**
+ * Get the children the specified folder node. If the folder has not yet been loaded, then load it.
+ *
+ * @param node The folder node.
+ * @return The children of the folder node.
+ * @throws TCFException Thrown during querying the children nodes.
+ */
+ public List<FSTreeNode> getChildren(final FSTreeNode node) throws TCFException, InterruptedException {
+ if(node.childrenQueried) {
+ return node.getChildren();
+ }
+ IChannel channel = null;
+ try {
+ channel = openChannel(node.peerNode.getPeer());
+ IFileSystem service = getBlockingFileSystem(channel);
+ if (service != null) {
+ return getChildren(node, service);
+ }
+ String message = NLS.bind(Messages.Operation_NoFileSystemError, node.peerNode.getPeerId());
+ throw new TCFFileSystemException(IStatus.ERROR, message);
+ }
+ finally {
+ if (channel != null) Tcf.getChannelManager().closeChannel(channel);
+ }
+ }
+
+ /**
+ * Load the children of the specified folder node using the file system service.
+ *
+ * @param node The folder node.
+ * @param service The file system service.
+ * @throws TCFFileSystemException Thrown during querying the children nodes.
+ */
+ protected void loadChildren(final FSTreeNode node, final IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ List<FSTreeNode> children = queryChildren(node, service);
+ node.addChidren(children);
+ node.queryDone();
+ }
+
+ /**
+ * Query the children of the specified node using the file system service.
+ *
+ * @param node The folder node.
+ * @param service The file system service.
+ * @return The children of the folder node.
+ * @throws TCFFileSystemException Thrown during querying the children nodes.
+ */
+ protected List<FSTreeNode> queryChildren(final FSTreeNode node, final IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if(monitor.isCanceled()) throw new InterruptedException();
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final IFileHandle[] handles = new IFileHandle[1];
+ try {
+ String dir = node.getLocation();
+ service.opendir(dir, new DoneOpen() {
+ @Override
+ public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) {
+ if (error != null) {
+ String message = NLS.bind(Messages.Operation_CannotOpenDir, node.name, error);
+ errors[0] = new TCFFileSystemException(IStatus.WARNING, message, error);
+ }
+ else {
+ handles[0] = handle;
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ errors[0] = null;
+ final List<FSTreeNode> children = new ArrayList<FSTreeNode>();
+ final boolean[] eofs = new boolean[1];
+ while (!eofs[0]) {
+ service.readdir(handles[0], new DoneReadDir() {
+ @Override
+ public void doneReadDir(IToken token, FileSystemException error, DirEntry[] entries, boolean eof) {
+ if (eof) {
+ eofs[0] = true;
+ }
+ if (error == null) {
+ if (entries != null && entries.length > 0) {
+ for (DirEntry entry : entries) {
+ FSTreeNode childNode = new FSTreeNode(node, entry, false);
+ children.add(childNode);
+ }
+ }
+ }
+ else {
+ errors[0] = newTCFException(IStatus.INFO, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }
+ return children;
+ }
+ finally {
+ if (handles[0] != null) {
+ service.close(handles[0], new IFileSystem.DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Remove the child from the children list of the specified folder. If the folder has not yet
+ * expanded, then expand it.
+ *
+ * @param service The file system service.
+ * @param folder The folder node from which the node is going to be removed.
+ * @param child The child node to be removed.
+ * @throws TCFFileSystemException Thrown during children querying.
+ */
+ protected void removeChild(final IFileSystem service, final FSTreeNode folder, final FSTreeNode child) throws TCFFileSystemException, InterruptedException {
+ if (Protocol.isDispatchThread()) {
+ if (!folder.childrenQueried) {
+ loadChildren(folder, service);
+ }
+ folder.removeChild(child);
+ child.setParent(null);
+ }
+ else {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final InterruptedException[] iexs = new InterruptedException[1];
+ Protocol.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ removeChild(service, folder, child);
+ }
+ catch (TCFFileSystemException e) {
+ errors[0] = e;
+ }
+ catch (InterruptedException e) {
+ iexs[0] = e;
+ }
+ }
+ });
+ if (errors[0] != null) throw errors[0];
+ if (iexs[0] != null) throw iexs[0];
+ }
+ }
+
+ /**
+ * Find the node with the name from the children list of the folder.
+ *
+ * @param service The file system service.
+ * @param folder The folder node.
+ * @param name The target node's name.
+ * @return The node with the specified name or null if no such node is found.
+ * @throws TCFFileSystemException Thrown when querying the children.
+ */
+ protected FSTreeNode findChild(IFileSystem service, FSTreeNode folder, String name) throws TCFFileSystemException, InterruptedException {
+ List<FSTreeNode> children = getChildren(folder, service);
+ for (FSTreeNode child : children) {
+ if (child.name.equals(name)) return child;
+ }
+ return null;
+ }
+
+ /**
+ * Create the name for the target file that is copied. If there exists a file with the same
+ * name, then "Copy of xxxx" and "Copy (n) of xxxx" will be used as the target file name.
+ *
+ * @param service File system service used to query the children nodes of the folder.
+ * @param node The node whose target file is to be created.
+ * @param dest The destination folder.
+ * @return The new target node with the new name following the rule.
+ * @throws TCFFileSystemException Thrown during children querying.
+ */
+ protected FSTreeNode createCopyDestination(IFileSystem service, FSTreeNode node, FSTreeNode dest) throws TCFFileSystemException, InterruptedException {
+ FSTreeNode copy = (FSTreeNode) node.clone();
+ String name = node.name;
+ FSTreeNode possibleChild = findChild(service, dest, name);
+ for (int n = 0; possibleChild != null; n++) {
+ if (n > 0) {
+ name = NLS.bind(Messages.Operation_CopyNOfFile, Integer.valueOf(n), node.name);
+ }
+ else {
+ name = NLS.bind(Messages.Operation_CopyOfFile, node.name);
+ }
+ possibleChild = findChild(service, dest, name);
+ }
+ copy.name = name;
+ addChild(service, dest, copy);
+ return copy;
+ }
+
+ /**
+ * Make a new directory with for the new node.
+ *
+ * @param service The file system service.
+ * @param node The directory node to be made.
+ * @throws TCFFileSystemException Thrown during children querying.
+ */
+ protected void mkdir(IFileSystem service, final FSTreeNode node) throws TCFFileSystemException {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ String path = node.getLocation(true);
+ service.mkdir(path, node.attr, new DoneMkDir() {
+ @Override
+ public void doneMkDir(IToken token, FileSystemException error) {
+ if (error != null) {
+ String message = NLS
+ .bind(Messages.Operation_CannotCreateDirectory, new Object[] { node.name, error });
+ errors[0] = new TCFFileSystemException(IStatus.WARNING, message, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }
+
+ /**
+ * Confirm if the file/folder represented by the specified should be replaced.
+ *
+ * @param node The file/folder node.
+ * @return The confirming result. true yes, false no.
+ * @throws InterruptedException Thrown when canceled.
+ */
+ protected boolean confirmReplace(final FSTreeNode node, IConfirmCallback confirmCallback) throws InterruptedException {
+ if(confirmCallback == null) return true;
+ if (yes2All) return true;
+ int result = confirmCallback.confirms(node);
+ switch (result) {
+ case 0:
+ return true;
+ case 1:
+ yes2All = true;
+ return true;
+ case 2:
+ return false;
+ }
+ throw new InterruptedException();
+ }
+
+ /**
+ * Add the specified child to the folder node's children list.
+ *
+ * @param service The file system service.
+ * @param folder The folder node.
+ * @param child The child node to be added.
+ * @throws TCFFileSystemException Thrown during children querying.
+ */
+ protected void addChild(final IFileSystem service, final FSTreeNode folder, final FSTreeNode child) throws TCFFileSystemException, InterruptedException {
+ if (Protocol.isDispatchThread()) {
+ if (!folder.childrenQueried) {
+ loadChildren(folder, service);
+ }
+ child.setParent(folder);
+ folder.addChild(child);
+ }
+ else {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final InterruptedException[] iexs = new InterruptedException[1];
+ Protocol.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ addChild(service, folder, child);
+ }
+ catch (TCFFileSystemException e) {
+ errors[0] = e;
+ }
+ catch (InterruptedException e) {
+ iexs[0] = e;
+ }
+ }
+ });
+ if (errors[0] != null) throw errors[0];
+ if (iexs[0] != null) throw iexs[0];
+ }
+ }
+
+ /**
+ * Remove the file.
+ *
+ * @param node
+ * @param service
+ * @throws TCFFileSystemException
+ */
+ protected void removeFile(final FSTreeNode node, IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if (monitor.isCanceled()) throw new InterruptedException();
+ // Do the actual deleting.
+ String path = node.getLocation(true);
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ service.remove(path, new DoneRemove() {
+ @Override
+ public void doneRemove(IToken token, FileSystemException error) {
+ if (error == null) {
+ cleanUpFile(node);
+ }
+ else {
+ errors[0] = newTCFException(IStatus.WARNING, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }
+
+ /**
+ * Remove the folder.
+ *
+ * @param node
+ * @param service
+ * @throws TCFFileSystemException
+ */
+ protected void removeFolder(final FSTreeNode node, IFileSystem service) throws TCFFileSystemException, InterruptedException {
+ if (monitor.isCanceled()) throw new InterruptedException();
+ // Do the actual deleting.
+ String path = node.getLocation(true);
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ service.rmdir(path, new DoneRemove() {
+ @Override
+ public void doneRemove(IToken token, FileSystemException error) {
+ if (error == null) {
+ cleanUpFolder(node);
+ }
+ else {
+ errors[0] = newTCFException(IStatus.WARNING, error);
+ }
+ }
+ });
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ this.monitor = monitor;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName()
+ */
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork()
+ */
+ @Override
+ public int getTotalWork() {
+ return IProgressMonitor.UNKNOWN;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.core.utils.Ancestor#getParent(java.lang.Object)
+ */
+ @Override
+ protected FSTreeNode getParent(FSTreeNode element) {
+ return element.getParent();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/FSTreeNodePropertyTester.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/FSTreeNodePropertyTester.java
new file mode 100644
index 000000000..ec31cf72c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/FSTreeNodePropertyTester.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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) - [345387]Open the remote files with a proper editor
+ * William Chen (Wind River) - [352302]Opening a file in an editor depending on
+ * the client's permissions.
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.testers;
+
+import java.io.File;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.ContentTypeHelper;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.CacheState;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The property tester of an FSTreeNode. The properties include "isFile"
+ * if it is a file node, "isDirectory" if it is a directory, "isBinaryFile"
+ * if it is a binary file, "isReadable" if it is readable, "isWritable" if
+ * it is writable, "isExecutable" if it is executable, "isRoot" if it is a
+ * root directory, "isWindows" if it is a windows file node, "isReadOnly"
+ * if it is read only, "isHidden" if it is hidden, "getCacheState" to
+ * get a node's state.
+ * <p>
+ * "testParent" is a property by which the parent or even the grand parent
+ * of a node can be tested. The arguments is a recursive list of the above
+ * test property including "testParent".
+ * <p>
+ * The following is an example of how it is used.
+ * <pre>
+ * &lt;test
+ * args="isWritable"
+ * property="org.eclipse.tcf.te.tcf.filesystem.propertytester.treenode.testParent"&gt;
+ * &lt;/test&gt;
+ * </pre>
+ * <p>
+ * The above example tests if the parent node is writable.
+ * <pre>
+ * &lt;test
+ * args="testParent,isWritable"
+ * property="org.eclipse.tcf.te.tcf.filesystem.propertytester.treenode.testParent"&gt;
+ * &lt;/test&gt;
+ * </pre>
+ * <p>
+ * The above example tests if the grand parent node is writable.
+ * <p>
+ * And so on, you can test its ancestor recursively:
+ * <pre>
+ * &lt;test
+ * args="testParent,testParent,testParent,...,isWritable"
+ * property="org.eclipse.tcf.te.tcf.filesystem.propertytester.treenode.testParent"&gt;
+ * &lt;/test&gt;
+ * </pre>
+ */
+public class FSTreeNodePropertyTester extends PropertyTester {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object, java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ @Override
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if(receiver == null)
+ return false;
+ Assert.isTrue(receiver instanceof FSTreeNode);
+ FSTreeNode node = (FSTreeNode) receiver;
+ if (property.equals("isFile")) { //$NON-NLS-1$
+ return node.isFile();
+ } else if (property.equals("isDirectory")) { //$NON-NLS-1$
+ return node.isDirectory();
+ } else if (property.equals("isBinaryFile")) { //$NON-NLS-1$
+ return ContentTypeHelper.isBinaryFile(node);
+ } else if (property.equals("isReadable")){ //$NON-NLS-1$
+ return node.isReadable();
+ } else if (property.equals("isWritable")){ //$NON-NLS-1$
+ return node.isWritable();
+ } else if (property.equals("isExecutable")){ //$NON-NLS-1$
+ return node.isExecutable();
+ } else if (property.equals("isRoot")) { //$NON-NLS-1$
+ return node.isRoot();
+ } else if (property.equals("isSystemRoot")) { //$NON-NLS-1$
+ return node.isSystemRoot();
+ } else if (property.equals("isWindows")) { //$NON-NLS-1$
+ return node.isWindowsNode();
+ } else if (property.equals("isReadOnly")) { //$NON-NLS-1$
+ return node.isReadOnly();
+ } else if (property.equals("isHidden")) { //$NON-NLS-1$
+ return node.isHidden();
+ } else if (property.equals("testParent")) { //$NON-NLS-1$
+ return testParent(node, args, expectedValue);
+ } else if (property.equals("getCacheState")){ //$NON-NLS-1$
+ File file = CacheManager.getCacheFile(node);
+ if(!file.exists())
+ return false;
+ CacheState state = node.getCacheState();
+ return state.name().equals(expectedValue);
+ }
+ return false;
+ }
+
+ private boolean testParent(FSTreeNode node, Object[] args, Object expectedValue) {
+ if(args == null || args.length == 0)
+ return false;
+ String arg = (String) args[0];
+ Object[] newArgs = new Object[args.length -1];
+ System.arraycopy(args, 1, newArgs, 0, args.length - 1);
+ return test(node.getParent(), arg, newArgs, expectedValue);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/TargetPropertyTester.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/TargetPropertyTester.java
new file mode 100644
index 000000000..3b6ca32b0
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/testers/TargetPropertyTester.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.testers;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+
+/**
+ * The property tester to test if the target OS is a Windows OS.
+ */
+public class TargetPropertyTester extends PropertyTester {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object, java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ @Override
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if(receiver instanceof IPeerNode) {
+ final IPeerNode peerNode = (IPeerNode) receiver;
+ if(property.equals("isWindows")) { //$NON-NLS-1$
+ return isWindows(peerNode);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test if the target represented by the peer model is a windows target.
+ *
+ * @param peerNode The peer model of the target.
+ * @return true if it is a windows target.
+ */
+ public static boolean isWindows(final IPeerNode peerNode) {
+ final String osName = getOSName(peerNode);
+ return osName == null ? false : (osName.startsWith("Windows")); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the OS name from the peer model.
+ *
+ * @param peerNode The peer model.
+ * @return OS name.
+ */
+ public static String getOSName(final IPeerNode peerNode) {
+ final String[] osName = new String[1];
+ if (Protocol.isDispatchThread()) {
+ osName[0] = peerNode.getPeer().getOSName();
+ }
+ else {
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ osName[0] = peerNode.getPeer().getOSName();
+ }
+ });
+ }
+ return osName[0];
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfInputStream.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfInputStream.java
new file mode 100644
index 000000000..613bf6f64
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfInputStream.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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:
+ * William Chen (Wind River)- [345387]Open the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.url;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem.DoneRead;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The TCF input stream returned by {@link TcfURLConnection#getInputStream()}.
+ */
+public class TcfInputStream extends InputStream {
+ // Default chunk size while pumping the data.
+ private static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
+
+ // Current reading position
+ long position;
+ // The byte array used to buffer data.
+ byte[] buffer;
+ // The offset being read in the buffer.
+ int offset;
+
+ // If the reading has reached the end of the file.
+ boolean EOF;
+ // If the stream has been closed.
+ boolean closed;
+ // The current error during reading.
+ Exception ERROR;
+
+ // The chunk size of the reading buffer.
+ int chunk_size = 0;
+
+ // The URL Connection
+ TcfURLConnection connection;
+
+ /**
+ * Create a TCF input stream connected the specified peer with specified
+ * path to the remote resource.
+ *
+ * @param peer
+ * The TCF agent peer.
+ * @param path
+ * The path to the remote resource.
+ */
+ public TcfInputStream(TcfURLConnection connection) {
+ this(connection, DEFAULT_CHUNK_SIZE);
+ }
+
+ /**
+ * Create a TCF input stream connected the specified peer with specified
+ * path to the remote resource using the specified buffer size.
+ *
+ * @param peer
+ * The TCF agent peer.
+ * @param path
+ * The path to the remote resource.
+ * @param chunk_size
+ * The buffer size.
+ */
+ public TcfInputStream(TcfURLConnection connection, int chunk_size) {
+ this.connection = connection;
+ this.chunk_size = chunk_size;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.io.InputStream#read()
+ */
+ @Override
+ public int read() throws IOException {
+ if (closed)
+ throw new IOException(Messages.TcfInputStream_StreamClosed);
+ if (ERROR != null) {
+ IOException exception = new IOException(ERROR.toString());
+ exception.initCause(ERROR);
+ throw exception;
+ }
+ if (buffer == null) {
+ if (EOF) {
+ return -1;
+ }
+ readBlock();
+ return read();
+ }
+ if (EOF) {
+ if (offset == buffer.length) {
+ return -1;
+ }
+ // Note that convert the byte to an integer correctly
+ return 0xff & buffer[offset++];
+ }
+ if (offset == buffer.length) {
+ readBlock();
+ return read();
+ }
+ // Note that convert the byte to an integer correctly
+ return 0xff & buffer[offset++];
+ }
+
+ /**
+ * Read a block of data into the buffer. Reset the offset, increase the
+ * current position and remember the EOF status. If there's an error,
+ * remember it for read() to check.
+ */
+ private void readBlock() {
+ connection.service.read(connection.handle, position, chunk_size, new DoneRead() {
+ @Override
+ public void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof) {
+ if (error != null) {
+ ERROR = error;
+ }
+ if (data == null) {
+ ERROR = new IOException(Messages.TcfInputStream_NoDataAvailable);
+ }
+ EOF = eof;
+ buffer = data;
+ if (buffer != null)
+ position += buffer.length;
+ offset = 0;
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.io.InputStream#close()
+ */
+ @Override
+ public void close() throws IOException {
+ if (!closed) {
+ connection.closeStream(this);
+ closed = true;
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfOutputStream.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfOutputStream.java
new file mode 100644
index 000000000..6f1c7e202
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfOutputStream.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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:
+ * William Chen (Wind River) - [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.url;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem.DoneWrite;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The TCF output stream returned by {@link TcfURLConnection#getOutputStream()}.
+ */
+public class TcfOutputStream extends OutputStream {
+ // Default chunk size while pumping the data.
+ private static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
+
+ // Current writing position
+ long position;
+ // The byte array used to buffer data.
+ byte[] buffer;
+ // The offset being written in the buffer.
+ int offset;
+
+ // If the stream has been closed.
+ boolean closed;
+ // The current error during writing.
+ Exception ERROR;
+
+ // The URL Connection
+ TcfURLConnection connection;
+ /**
+ * Create a TCF output stream connected the specified peer with specified
+ * path to the remote resource.
+ *
+ * @param peer
+ * The TCF agent peer.
+ * @param path
+ * The path to the remote resource.
+ */
+ public TcfOutputStream(TcfURLConnection connection) {
+ this(connection, DEFAULT_CHUNK_SIZE);
+ }
+
+ /**
+ * Create a TCF output stream connected the specified peer with specified
+ * path to the remote resource using the specified buffer size.
+ *
+ * @param peer
+ * The TCF agent peer.
+ * @param path
+ * The path to the remote resource.
+ * @param chunk_size
+ * The buffer size.
+ */
+ public TcfOutputStream(TcfURLConnection connection, int chunk_size) {
+ this.connection = connection;
+ buffer = new byte[chunk_size];
+ offset = 0;
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(int)
+ */
+ @Override
+ public void write(int b) throws IOException {
+ if (closed)
+ throw new IOException(Messages.TcfOutputStream_StreamClosed);
+ if (ERROR != null) {
+ IOException exception = new IOException(ERROR.toString());
+ exception.initCause(ERROR);
+ throw exception;
+ }
+ if (offset < buffer.length) {
+ buffer[offset++] = (byte) b;
+ }
+ if (offset == buffer.length)
+ flush();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#flush()
+ */
+ @Override
+ public void flush() throws IOException {
+ if (offset > 0) {
+ connection.service.write(connection.handle, position, buffer, 0, offset, new DoneWrite() {
+ @Override
+ public void doneWrite(IToken token, FileSystemException error) {
+ if (error != null) {
+ ERROR = error;
+ }
+ position += offset;
+ offset = 0;
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#close()
+ */
+ @Override
+ public void close() throws IOException {
+ if (!closed) {
+ connection.closeStream(this);
+ closed = true;
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLConnection.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLConnection.java
new file mode 100644
index 000000000..3d1e53126
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLConnection.java
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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:
+ * William Chen (Wind River)- [345387]Open the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.url;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DoneClose;
+import org.eclipse.tcf.services.IFileSystem.DoneOpen;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFChannelException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+import org.eclipse.tcf.te.tcf.locator.interfaces.services.IPeerModelLookupService;
+import org.eclipse.tcf.te.tcf.locator.model.ModelManager;
+
+/**
+ * The URL connection returned by TCF stream service used to handle "tcf"
+ * stream protocol.
+ */
+public class TcfURLConnection extends URLConnection {
+ // Default connecting timeout.
+ private static final int DEFAULT_CONNECT_TIMEOUT = 5000;
+ // Default file opening timeout.
+ private static final int DEFAULT_OPEN_TIMEOUT = 5000;
+ // Default file reading timeout.
+ private static final int DEFAULT_READ_TIMEOUT = 5000;
+ // Default file closing timeout.
+ private static final int DEFAULT_CLOSE_TIMEOUT = 5000;
+ // The schema name of the stream protocol.
+ public static final String PROTOCOL_SCHEMA = "tcf"; //$NON-NLS-1$
+
+ // The input stream of this connection.
+ private TcfInputStream inputStream;
+ // The output stream of this connection.
+ private TcfOutputStream outputStream;
+
+ // The TCF agent peer of the connection.
+ private IPeer peer;
+ // The path to the resource on the remote file system.
+ String path;
+ // The timeout for opening a file.
+ private int openTimeout;
+ // The timeout for closing a file.
+ private int closeTimeout;
+
+ // The TCF channel used to open and read the resource.
+ IChannel channel;
+ // The file's handle
+ IFileHandle handle;
+ // The file service
+ IFileSystem service;
+
+ /**
+ * Create a TCF URL Connection using the specified url. The format of this
+ * URL should be: tcf:/<TCF_AGENT_ID>/remote/path/to/the/resource... The
+ * stream protocol schema is designed in this way in order to retrieve the
+ * agent peer ID without knowing the structure of a TCF peer id.
+ *
+ * @see TcfURLStreamHandlerService#parseURL(URL, String, int, int)
+ * @param url
+ * The URL of the resource.
+ */
+ public TcfURLConnection(final URL url) {
+ super(url);
+ // The peerId is stored as the host name in URL. See TcfURLStreamHandlerService#parseURL for details.
+ String peerId = url.getHost();
+ Assert.isNotNull(peerId);
+ peer = findPeer(peerId);
+ if(peer == null) {
+ throw new IllegalArgumentException(NLS.bind(Messages.TcfURLConnection_NoPeerFound, peerId));
+ }
+ path = url.getPath();
+ // Set default timeout.
+ setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
+ setOpenTimeout(DEFAULT_OPEN_TIMEOUT);
+ setReadTimeout(DEFAULT_READ_TIMEOUT);
+ setCloseTimeout(DEFAULT_CLOSE_TIMEOUT);
+ }
+
+ /**
+ * Find the TCF peer with the specified ID.
+ *
+ * @param peerId The target peer's ID.
+ * @return The peer with this ID or null if not found.
+ */
+ private IPeer findPeer(final String peerId) {
+ Assert.isNotNull(peerId);
+
+ final AtomicReference<IPeer> peer = new AtomicReference<IPeer>();
+
+ Runnable runnable = new Runnable() {
+
+ @Override
+ public void run() {
+ IPeer p = Protocol.getLocator().getPeers().get(peerId);
+ if (p == null) {
+ IPeerNode peerNode = ModelManager.getPeerModel().getService(IPeerModelLookupService.class).lkupPeerModelById(peerId);
+ if (peerNode != null) p = peerNode.getPeer();
+ }
+ peer.set(p);
+ }
+ };
+
+ if (Protocol.isDispatchThread()) runnable.run();
+ else Protocol.invokeAndWait(runnable);
+
+ return peer.get();
+ }
+
+ /**
+ * Get the timeout for closing a file.
+ *
+ * @return the timeout in milliseconds.
+ */
+ public long getCloseTimeout() {
+ return closeTimeout;
+ }
+
+ /**
+ * Set the timeout for closing a file.
+ *
+ * @param closeTimeout
+ * the timeout in milliseconds.
+ */
+ public void setCloseTimeout(int closeTimeout) {
+ this.closeTimeout = closeTimeout;
+ }
+
+ /**
+ * Get the timeout for opening a file.
+ *
+ * @return the timeout in milliseconds.
+ */
+ public long getOpenTimeout() {
+ return openTimeout;
+ }
+
+ /**
+ * Set the timeout for opening a file.
+ *
+ * @param openTimeout
+ * the timeout in milliseconds.
+ */
+ public void setOpenTimeout(int openTimeout) {
+ this.openTimeout = openTimeout;
+ }
+
+ /**
+ * Open a file on the remote file system for read/write and store the file handle.
+ *
+ * @throws IOException Opening file fails.
+ */
+ private void openFile() throws IOException {
+ if(peer == null)
+ throw new IOException(Messages.TcfURLConnection_NoSuchTcfAgent);
+ try {
+ // Open the channel
+ channel = Operation.openChannel(peer);
+ } catch (TCFChannelException e) {
+ throw new IOException(e.getMessage());
+ }
+ if (channel != null) {
+ service = Operation.getBlockingFileSystem(channel);
+ if (service != null) {
+ final FileSystemException[] errors = new FileSystemException[1];
+ // Open the file.
+ int open_flag = 0;
+ if (doInput)
+ open_flag |= IFileSystem.TCF_O_READ;
+ if (doOutput)
+ open_flag |= IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC;
+ service.open(path, open_flag, null, new DoneOpen() {
+ @Override
+ public void doneOpen(IToken token, FileSystemException error, IFileHandle hdl) {
+ errors[0] = error;
+ handle = hdl;
+ }
+ });
+ if (errors[0] != null) {
+ IOException exception = new IOException(errors[0].toString());
+ exception.initCause(errors[0]);
+ throw exception;
+ }
+ if (handle == null) {
+ throw new IOException(Messages.TcfURLConnection_NoFileHandleReturned);
+ }
+ } else {
+ throw new IOException(Messages.Operation_NoFileSystemError);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.net.URLConnection#connect()
+ */
+ @Override
+ public void connect() throws IOException {
+ if (!connected) {
+ openFile();
+ if (doInput) {
+ inputStream = new TcfInputStream(this);
+ }
+ if (doOutput) {
+ outputStream = new TcfOutputStream(this);
+ }
+ connected = true;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.net.URLConnection#getInputStream()
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (!connected)
+ connect();
+ return inputStream;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.net.URLConnection#getOutputStream()
+ */
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ if (!connected)
+ connect();
+ return outputStream;
+ }
+
+ /**
+ * Close the stream, release its file handler and close
+ * the TCF channel used if possible.
+ *
+ * @param stream The stream either the input stream or the output stream.
+ * @throws IOException If closing file handle times out.
+ */
+ public synchronized void closeStream(Closeable stream) throws IOException {
+ boolean shouldClose = shouldCloseFileHandle(stream);
+ if (shouldClose) {
+ service.close(handle, new DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ Tcf.getChannelManager().closeChannel(channel);
+ }
+ });
+ }
+ }
+
+ /**
+ * Decide if the file handle and the TCF channel should be closed if
+ * the specified stream is closed. If the stream is the last stream
+ * that depends on the file handle and the TCF channel, then it should
+ * be closed.
+ *
+ * @param stream The stream to be closed.
+ * @return true if the file handle and the TCF channel should be closed.
+ */
+ private boolean shouldCloseFileHandle(Closeable stream) {
+ boolean shouldClose = false;
+ if (stream == inputStream) {
+ if (doOutput) {
+ if (outputStream.closed) {
+ shouldClose = true;
+ }
+ } else {
+ shouldClose = true;
+ }
+ } else if (stream == outputStream) {
+ if (doInput) {
+ if (inputStream.closed)
+ shouldClose = true;
+ } else {
+ shouldClose = true;
+ }
+ }
+ return shouldClose;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLStreamHandlerService.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLStreamHandlerService.java
new file mode 100644
index 000000000..dabe51149
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/url/TcfURLStreamHandlerService.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.url;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.StringTokenizer;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+import org.osgi.service.url.AbstractURLStreamHandlerService;
+
+/**
+ * The stream handler service used to parse tcf stream protocol.
+ */
+public class TcfURLStreamHandlerService extends AbstractURLStreamHandlerService {
+ // The pattern of a windows path.
+ private static final String WINPATH_PATTERN = "[A-Za-z]:.*"; //$NON-NLS-1$
+ private static final char[] WINPATH_FORBIDDEN_CHARS = {':', '*', '?', '"', '<', '>', '|' };
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.service.url.AbstractURLStreamHandlerService#openConnection(java.net.URL)
+ */
+ @Override
+ public URLConnection openConnection(URL u) throws IOException {
+ return new TcfURLConnection(u);
+ }
+
+ /**
+ * Parse the given spec to the specified URL object. The expected format is:
+ * <p>
+ *
+ * <pre>
+ * TCF_URL = tcf:/<strong>PEER_ID</strong>/(<strong>URL_PATH</strong>)?
+ * PEER_ID = (.^/)+
+ * URL_PATH = <strong>WIN_PATH</strong> | <strong>RELATIVE_PATH</strong>
+ * WIN_PATH = <strong>DISK_SEG</strong> / (<strong>RELATIVE_PATH</strong>)?
+ * DISK_SEG = [a-zA-Z]:
+ * RELATIVE_PATH = <strong>PATH_SEG</strong> | <strong>PATH_SEG</strong>/<strong>RELATIVE_PATH</strong>
+ * Unix/Linux PATH_SEG = (.^[/])+
+ * Windows PATH_SEG = (.^[\/:*?"<>|])+
+ * </pre>
+ */
+ @Override
+ protected void parseURL(URL u, String spec, int start, int limit) {
+ if (u.getPath() != null) {
+ String path = u.getPath();
+ if (!path.endsWith("/")) { //$NON-NLS-1$
+ path += "/"; //$NON-NLS-1$
+ }
+ path += spec;
+ setURL(u, u.getProtocol(), u.getHost(), u.getPort(), u.getAuthority(), u.getUserInfo(), path, u.getQuery(), u.getRef());
+ }
+ else {
+ IllegalArgumentException errorFormat = new IllegalArgumentException(Messages.TcfURLStreamHandlerService_ErrorURLFormat);
+ int end = spec.indexOf("/", start); //$NON-NLS-1$
+ if (end == -1) throw errorFormat;
+ start = end + 1;
+ end = spec.indexOf("/", start); //$NON-NLS-1$
+ if (end == -1) throw errorFormat;
+ String peerId = spec.substring(start, end);
+ if (peerId.trim().length() == 0) throw errorFormat;
+ start = end + 1;
+ String path = spec.substring(start);
+ if (path.length() > 0) {
+ if (path.matches(WINPATH_PATTERN)) {
+ String pathext = path.substring(2); // Cut the path after ':'.
+ if (pathext.length() == 0) throw new IllegalArgumentException(Messages.TcfURLStreamHandlerService_OnlyDiskPartError);
+ pathext = pathext.substring(1); // Cut the path after the disk part.
+ checkWinPath(pathext);
+ }
+ else {
+ path = "/" + path; //$NON-NLS-1$
+ }
+ }
+ else {
+ path = "/"; //$NON-NLS-1$
+ }
+ final String path2decode = path;
+ final AtomicReference<String> pathRef = new AtomicReference<String>();
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ @Override
+ public void run() throws Exception {
+ pathRef.set(decodeURLPath(path2decode));
+ }});
+ path = pathRef.get();
+ setURL(u, TcfURLConnection.PROTOCOL_SCHEMA, peerId, -1, null, null, path, null, null);
+ }
+ }
+
+ /**
+ * Decode the path from URI compatible path to a
+ * file system path.
+ *
+ * @see FSTreeNode#getURLEncodedPath
+ * @param path The URL whose path is to be decoded.
+ * @return The file system path.
+ * @throws UnsupportedEncodingException
+ */
+ String decodeURLPath(String path) throws UnsupportedEncodingException {
+ StringTokenizer st = new StringTokenizer(path, "/"); //$NON-NLS-1$
+ StringBuilder builder = new StringBuilder();
+ while(st.hasMoreTokens()) {
+ if(builder.length() > 0) {
+ builder.append("/"); //$NON-NLS-1$
+ }
+ String segment = st.nextToken();
+ builder.append(URLDecoder.decode(segment, "UTF-8")); //$NON-NLS-1$
+ }
+ String relative = builder.toString();
+ return path.startsWith("/") ? "/" + relative : relative; //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ /**
+ * Check the format of the specified windows path.
+ *
+ * @param path The relative path to a disk part.
+ */
+ private void checkWinPath(String path) {
+ for (int i = 0; i < path.length(); i++) {
+ char c = path.charAt(i);
+ for(int j=0;j<WINPATH_FORBIDDEN_CHARS.length;j++) {
+ if(c==WINPATH_FORBIDDEN_CHARS[j]) {
+ throw new IllegalArgumentException(NLS.bind(Messages.TcfURLStreamHandlerService_IllegalCharacter, "'"+c+"'")); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+ }
+ }
+
+ /**
+ * Encode the path from a file system path to
+ * URI compatible path.
+ *
+ * @see FSTreeNode#getURLEncodedPath
+ * @param path The URL whose path is to be decoded.
+ * @return The file system path.
+ * @throws UnsupportedEncodingException
+ */
+ String encodeURLPath(String path) throws UnsupportedEncodingException {
+ StringTokenizer st = new StringTokenizer(path, "/"); //$NON-NLS-1$
+ StringBuilder builder = new StringBuilder();
+ while(st.hasMoreTokens()) {
+ if(builder.length() > 0) {
+ builder.append("/"); //$NON-NLS-1$
+ String segment = st.nextToken();
+ builder.append(URLEncoder.encode(segment, "UTF-8")); //$NON-NLS-1$
+ }
+ else {
+ String segment = st.nextToken();
+ if(path.matches(WINPATH_PATTERN)) {
+ builder.append(segment);
+ }
+ else{
+ builder.append(URLEncoder.encode(segment, "UTF-8")); //$NON-NLS-1$
+ }
+ }
+ }
+ String relative = builder.toString();
+ return path.startsWith("/") ? "/" + relative : relative; //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.service.url.AbstractURLStreamHandlerService#toExternalForm(java.net.URL)
+ */
+ @Override
+ public String toExternalForm(final URL u) {
+ String peerId = u.getHost();
+ StringBuilder builder = new StringBuilder();
+ builder.append(TcfURLConnection.PROTOCOL_SCHEMA);
+ builder.append(":/"); //$NON-NLS-1$
+ builder.append(peerId);
+ final AtomicReference<String> pathRef = new AtomicReference<String>();
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore
+ }
+ @Override
+ public void run() throws Exception {
+ pathRef.set(encodeURLPath(u.getPath()));
+ }});
+ String path = pathRef.get();
+ if(path == null) {
+ builder.append("/"); //$NON-NLS-1$
+ } else if(path.length() == 0) {
+ builder.append("/"); //$NON-NLS-1$
+ } else if(path.matches(WINPATH_PATTERN)) {
+ builder.append("/"); //$NON-NLS-1$
+ builder.append(path);
+ } else {
+ builder.append(path);
+ }
+ return builder.toString();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/BlockingFileSystemProxy.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/BlockingFileSystemProxy.java
new file mode 100644
index 000000000..98a4b189b
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/BlockingFileSystemProxy.java
@@ -0,0 +1,880 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
+
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.te.tcf.core.concurrent.Rendezvous;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * A blocking call proxy for a file system service. All calls to the service method are blocked
+ * until its "Done" handler is invoked.
+ * <p>
+ * <em>Note that all method call over the proxy must be made <b>OUTSIDE</b> of
+ * the dispatching thread.</em> If it is called inside of the dispatching thread, the call will be
+ * blocked forever.
+ * <p>
+ * This class is used to replace BlockingProxyCall for better debugability.
+ *
+ * @see BlockingCallProxy
+ */
+public class BlockingFileSystemProxy implements IFileSystem {
+ // The default timeout waiting for blocked invocations.
+ private static final long DEFAULT_TIMEOUT = Operation.DEFAULT_TIMEOUT;
+ // The actual object that provides file system services.
+ IFileSystem service;
+
+ /**
+ * Constructor with an delegating service.
+ *
+ * @param service The delegating service.
+ */
+ public BlockingFileSystemProxy(IFileSystem service) {
+ Assert.isNotNull(service);
+ this.service = service;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.protocol.IService#getName()
+ */
+ @Override
+ public String getName() {
+ return service.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#open(java.lang.String, int, org.eclipse.tcf.services.IFileSystem.FileAttrs, org.eclipse.tcf.services.IFileSystem.DoneOpen)
+ */
+ @Override
+ public IToken open(final String file_name, final int flags, final FileAttrs attrs, final DoneOpen done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.open(file_name, flags, attrs, new DoneOpen() {
+ @Override
+ public void doneOpen(final IToken token, final FileSystemException error, final IFileHandle handle) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneOpen(token, error, handle);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutOpeningFile, file_name));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#close(org.eclipse.tcf.services.IFileSystem.IFileHandle, org.eclipse.tcf.services.IFileSystem.DoneClose)
+ */
+ @Override
+ public IToken close(final IFileHandle handle, final DoneClose done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.close(handle, new DoneClose() {
+ @Override
+ public void doneClose(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneClose(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutClosingFile, handle));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#read(org.eclipse.tcf.services.IFileSystem.IFileHandle, long, int, org.eclipse.tcf.services.IFileSystem.DoneRead)
+ */
+ @Override
+ public IToken read(final IFileHandle handle, final long offset, final int len, final DoneRead done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.read(handle, offset, len, new DoneRead() {
+ @Override
+ public void doneRead(final IToken token, final FileSystemException error, final byte[] data, final boolean eof) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneRead(token, error, data, eof);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutReadingFile, handle));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#write(org.eclipse.tcf.services.IFileSystem.IFileHandle, long, byte[], int, int, org.eclipse.tcf.services.IFileSystem.DoneWrite)
+ */
+ @Override
+ public IToken write(final IFileHandle handle, final long offset, final byte[] data, final int data_pos, final int data_size, final DoneWrite done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.write(handle, offset, data, data_pos, data_size, new DoneWrite() {
+ @Override
+ public void doneWrite(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneWrite(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutWritingFile, handle));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#stat(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneStat)
+ */
+ @Override
+ public IToken stat(final String path, final DoneStat done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.stat(path, new DoneStat() {
+ @Override
+ public void doneStat(final IToken token, final FileSystemException error, final FileAttrs attrs) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneStat(token, error, attrs);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutStat, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#lstat(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneStat)
+ */
+ @Override
+ public IToken lstat(final String path, final DoneStat done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.lstat(path, new DoneStat() {
+ @Override
+ public void doneStat(final IToken token, final FileSystemException error, final FileAttrs attrs) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneStat(token, error, attrs);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutLstat, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#fstat(org.eclipse.tcf.services.IFileSystem.IFileHandle, org.eclipse.tcf.services.IFileSystem.DoneStat)
+ */
+ @Override
+ public IToken fstat(final IFileHandle handle, final DoneStat done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.fstat(handle, new DoneStat() {
+ @Override
+ public void doneStat(final IToken token, final FileSystemException error, final FileAttrs attrs) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneStat(token, error, attrs);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutFstat, handle));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#setstat(java.lang.String, org.eclipse.tcf.services.IFileSystem.FileAttrs, org.eclipse.tcf.services.IFileSystem.DoneSetStat)
+ */
+ @Override
+ public IToken setstat(final String path, final FileAttrs attrs, final DoneSetStat done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.setstat(path, attrs, new DoneSetStat() {
+ @Override
+ public void doneSetStat(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneSetStat(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutSetStat, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#fsetstat(org.eclipse.tcf.services.IFileSystem.IFileHandle, org.eclipse.tcf.services.IFileSystem.FileAttrs, org.eclipse.tcf.services.IFileSystem.DoneSetStat)
+ */
+ @Override
+ public IToken fsetstat(final IFileHandle handle, final FileAttrs attrs, final DoneSetStat done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.fsetstat(handle, attrs, new DoneSetStat() {
+ @Override
+ public void doneSetStat(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneSetStat(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutFSetStat, handle));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#opendir(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneOpen)
+ */
+ @Override
+ public IToken opendir(final String path, final DoneOpen done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.opendir(path, new DoneOpen() {
+ @Override
+ public void doneOpen(final IToken token, final FileSystemException error, final IFileHandle handle) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneOpen(token, error, handle);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutOpeningDir, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#readdir(org.eclipse.tcf.services.IFileSystem.IFileHandle, org.eclipse.tcf.services.IFileSystem.DoneReadDir)
+ */
+ @Override
+ public IToken readdir(final IFileHandle handle, final DoneReadDir done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.readdir(handle, new DoneReadDir() {
+ @Override
+ public void doneReadDir(final IToken token, final FileSystemException error, final DirEntry[] entries, final boolean eof) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneReadDir(token, error, entries, eof);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutReadingDir, handle));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#mkdir(java.lang.String, org.eclipse.tcf.services.IFileSystem.FileAttrs, org.eclipse.tcf.services.IFileSystem.DoneMkDir)
+ */
+ @Override
+ public IToken mkdir(final String path, final FileAttrs attrs, final DoneMkDir done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.mkdir(path, attrs, new DoneMkDir() {
+ @Override
+ public void doneMkDir(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneMkDir(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutMakingDir, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#rmdir(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneRemove)
+ */
+ @Override
+ public IToken rmdir(final String path, final DoneRemove done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.rmdir(path, new DoneRemove() {
+ @Override
+ public void doneRemove(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneRemove(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutRemovingDir, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#roots(org.eclipse.tcf.services.IFileSystem.DoneRoots)
+ */
+ @Override
+ public IToken roots(final DoneRoots done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.roots(new DoneRoots() {
+ @Override
+ public void doneRoots(final IToken token, final FileSystemException error, final DirEntry[] entries) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneRoots(token, error, entries);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(Messages.BlockingFileSystemProxy_TimeoutListingRoots);
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#remove(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneRemove)
+ */
+ @Override
+ public IToken remove(final String file_name, final DoneRemove done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.remove(file_name, new DoneRemove() {
+ @Override
+ public void doneRemove(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneRemove(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutRemovingFile, file_name));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#realpath(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneRealPath)
+ */
+ @Override
+ public IToken realpath(final String path, final DoneRealPath done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.realpath(path, new DoneRealPath() {
+ @Override
+ public void doneRealPath(final IToken token, final FileSystemException error, final String path) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneRealPath(token, error, path);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutGettingRealPath, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#rename(java.lang.String, java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneRename)
+ */
+ @Override
+ public IToken rename(final String old_path, final String new_path, final DoneRename done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.rename(old_path, new_path, new DoneRename() {
+ @Override
+ public void doneRename(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneRename(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutRenamingFile, old_path, new_path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#readlink(java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneReadLink)
+ */
+ @Override
+ public IToken readlink(final String path, final DoneReadLink done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.readlink(path, new DoneReadLink() {
+ @Override
+ public void doneReadLink(final IToken token, final FileSystemException error, final String path) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneReadLink(token, error, path);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutReadingLink, path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#symlink(java.lang.String, java.lang.String, org.eclipse.tcf.services.IFileSystem.DoneSymLink)
+ */
+ @Override
+ public IToken symlink(final String link_path, final String target_path, final DoneSymLink done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.symlink(link_path, target_path, new DoneSymLink() {
+ @Override
+ public void doneSymLink(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneSymLink(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutSymLink, link_path, target_path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#copy(java.lang.String, java.lang.String, boolean, boolean, org.eclipse.tcf.services.IFileSystem.DoneCopy)
+ */
+ @Override
+ public IToken copy(final String src_path, final String dst_path, final boolean copy_permissions, final boolean copy_ownership, final DoneCopy done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.copy(src_path, dst_path, copy_permissions, copy_ownership, new DoneCopy() {
+ @Override
+ public void doneCopy(final IToken token, final FileSystemException error) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneCopy(token, error);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(NLS.bind(Messages.BlockingFileSystemProxy_TimeoutCopying, src_path, dst_path));
+ }
+ return ref.get();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.services.IFileSystem#user(org.eclipse.tcf.services.IFileSystem.DoneUser)
+ */
+ @Override
+ public IToken user(final DoneUser done) {
+ Assert.isTrue(!Protocol.isDispatchThread());
+ final Rendezvous rendezvous = new Rendezvous();
+ final AtomicReference<IToken> ref = new AtomicReference<IToken>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ IToken token = service.user(new DoneUser() {
+ @Override
+ public void doneUser(final IToken token, final FileSystemException error, final int real_uid, final int effective_uid, final int real_gid, final int effective_gid, final String home) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ }
+
+ @Override
+ public void run() throws Exception {
+ done.doneUser(token, error, real_uid, effective_uid, real_gid, effective_gid, home);
+ }
+ });
+ rendezvous.arrive();
+ }
+ });
+ ref.set(token);
+ }
+ });
+ try {
+ rendezvous.waiting(DEFAULT_TIMEOUT);
+ }
+ catch (TimeoutException e) {
+ throw new RuntimeException(Messages.BlockingFileSystemProxy_TimeoutGettingUser);
+ }
+ return ref.get();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/CacheManager.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/CacheManager.java
new file mode 100644
index 000000000..c5749959f
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/CacheManager.java
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2013 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)- [345387] Open the remote files with a proper editor
+ * William Chen (Wind River)- [345552] Edit the remote files with a proper editor
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * The local file system cache used to manage the temporary files downloaded from a remote file
+ * system.
+ */
+public class CacheManager {
+ public static final char PATH_ESCAPE_CHAR = '$';
+
+ /**
+ * Get the local path of a node's cached file.
+ * <p>
+ * The preferred location is within the plugin's state location, in example
+ * <code>&lt;state location&gt;agent_<hashcode_of_peerId>/remote/path/to/the/file...</code>.
+ * <p>
+ * If the plug-in is loaded in a RCP workspace-less environment, the fall back strategy is to
+ * use the users home directory.
+ *
+ * @param node The file/folder node.
+ * @return The local path of the node's cached file.
+ */
+ public static IPath getCachePath(FSTreeNode node) {
+ File location = getCacheRoot();
+ String agentId = node.peerNode.getPeerId();
+ // Use Math.abs to avoid negative hash value.
+ String agent = agentId.replace(':', PATH_ESCAPE_CHAR);
+ IPath agentDir = new Path(location.getAbsolutePath()).append(agent);
+ File agentDirFile = agentDir.toFile();
+ mkdirChecked(agentDirFile);
+ return appendNodePath(agentDir, node);
+ }
+
+ /**
+ * Check and make a directory if it does not exist. Record the failure message if making fails.
+ *
+ * @param file The file to be deleted.
+ */
+ static void mkdirChecked(final File dir) {
+ if (!dir.exists()) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ if (!dir.mkdir()) {
+ throw new Exception(NLS.bind(Messages.CacheManager_MkdirFailed, dir
+ .getAbsolutePath()));
+ }
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ });
+ }
+ }
+
+ /**
+ * Check if the file exists and delete if it does. Record the failure message if deleting fails.
+ *
+ * @param file The file to be deleted.
+ */
+ static void deleteFileChecked(final File file) {
+ if (file.exists()) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ if (!file.delete()) {
+ throw new Exception(NLS.bind(Messages.Operation_DeletingFileFailed, file
+ .getAbsolutePath()));
+ }
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ });
+ }
+ }
+
+ /**
+ * Check if the file exists and set its read-only attribute if it does. Record the failure
+ * message if it fails.
+ *
+ * @param file The file to be set.
+ */
+ static void setReadOnlyChecked(final File file) {
+ if (file.exists()) {
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ if (!file.setReadOnly()) {
+ throw new Exception(NLS.bind(Messages.OpStreamOp_SetReadOnlyFailed, file
+ .getAbsolutePath()));
+ }
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ });
+ }
+ }
+
+ /**
+ * Get the local file of the specified node.
+ *
+ * <p>
+ * The preferred location is within the plugin's state location, in example
+ * <code>&lt;state location&gt;agent_<hashcode_of_peerId>/remote/path/to/the/file...</code>.
+ * <p>
+ * If the plug-in is loaded in a RCP workspace-less environment, the fall back strategy is to
+ * use the users home directory.
+ *
+ * @param node The file/folder node.
+ * @return The file object of the node's local cache.
+ */
+ public static File getCacheFile(FSTreeNode node) {
+ return getCachePath(node).toFile();
+ }
+
+ /**
+ * Get the cache file system's root directory on the local host's file system.
+ *
+ * @return The root folder's location of the cache file system.
+ */
+ public static File getCacheRoot() {
+ File location;
+ try {
+ location = CorePlugin.getDefault().getStateLocation().toFile();
+ }
+ catch (IllegalStateException e) {
+ // An RCP workspace-less environment (-data @none)
+ location = new File(System.getProperty("user.home"), ".tcf"); //$NON-NLS-1$ //$NON-NLS-2$
+ location = new File(location, "fs"); //$NON-NLS-1$
+ }
+
+ // Create the location if it not exist
+ mkdirChecked(location);
+ return location;
+ }
+
+ /**
+ * Append the path with the specified node's context path.
+ *
+ * @param path The path to be appended.
+ * @param node The file/folder node.
+ * @return The path to the node.
+ */
+ private static IPath appendNodePath(IPath path, FSTreeNode node) {
+ if (!node.isRoot() && node.getParent() != null) {
+ path = appendNodePath(path, node.getParent());
+ return appendPathSegment(node, path, node.name);
+ }
+ if (node.isWindowsNode()) {
+ String name = node.name;
+ name = name.substring(0, name.length() - 1);
+ name = name.replace(':', PATH_ESCAPE_CHAR);
+ return appendPathSegment(node, path, name);
+ }
+ return path;
+ }
+
+ /**
+ * Append the path with the segment "name". Create a directory if the node is a directory which
+ * does not yet exist.
+ *
+ * @param node The file/folder node.
+ * @param path The path to appended.
+ * @param name The segment's name.
+ * @return The path with the segment "name" appended.
+ */
+ private static IPath appendPathSegment(FSTreeNode node, IPath path, String name) {
+ IPath newPath = path.append(name);
+ File newFile = newPath.toFile();
+ if (node.isDirectory()) {
+ mkdirChecked(newFile);
+ }
+ return newPath;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/ContentTypeHelper.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/ContentTypeHelper.java
new file mode 100644
index 000000000..24914f38e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/ContentTypeHelper.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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:
+ * William Chen (Wind River)- [345387]Open the remote files with a proper editor
+ * William Chen (Wind River) [360494]Provide an "Open With" action in the pop
+ * up menu of file system nodes of Target Explorer.
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The content type helper used to provide helping methods about the content
+ * types of the files in the remote file system.
+ */
+public class ContentTypeHelper {
+ // The binary content type's id.
+ private static final String CONTENT_TYPE_BINARY_ID = "org.eclipse.cdt.core.binaryFile"; //$NON-NLS-1$
+
+ /**
+ * Judges if the node is a binary file.
+ *
+ * @param node
+ * The file node.
+ * @return true if the node is a binary file or else false.
+ */
+ public static boolean isBinaryFile(FSTreeNode node) {
+ IContentType contentType = getContentType(node);
+ if (contentType != null) {
+ IContentType binaryFile = Platform.getContentTypeManager()
+ .getContentType(CONTENT_TYPE_BINARY_ID);
+ if (binaryFile != null && contentType.isKindOf(binaryFile))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the content type of the specified file node.
+ *
+ * @param node
+ * The file node.
+ * @return The content type of the file node.
+ */
+ public static IContentType getContentType(FSTreeNode node) {
+ if (PersistenceManager.getInstance().isUnresovled(node))
+ // If it is already known unresolvable.
+ return null;
+ IContentType contentType = PersistenceManager.getInstance().getResolved(node);
+ if (contentType != null)
+ // If it is already known to have a certain content type.
+ return contentType;
+ // First check the content type by its name.
+ contentType = Platform.getContentTypeManager().findContentTypeFor(
+ node.name);
+ if (contentType == null) { // Then find the content type by its stream.
+ try {
+ contentType = findContentTypeByStream(node);
+ } catch (Exception e) {
+ }
+ }
+ if (contentType != null) { // If it is resolved, cache it.
+ PersistenceManager.getInstance().addResovled(node, contentType);
+ } else { // Or else, remember it as an unresolvable.
+ PersistenceManager.getInstance().addUnresolved(node);
+ }
+ return contentType;
+ }
+
+ /**
+ * Find the content type of the file using its content stream.
+ *
+ * @param node
+ * The file node.
+ * @return The content type of the file.
+ * @throws CoreException
+ * If the path of its local cache file couldn't be found.
+ * @throws IOException
+ * If something goes wrong during the content type parsing.
+ */
+ private static IContentType findContentTypeByStream(FSTreeNode node) throws CoreException, IOException {
+ InputStream is = null;
+ try {
+ File file = CacheManager.getCacheFile(node);
+ if (file.exists()) {
+ // If the local cache file exits.
+ IPath path = CacheManager.getCachePath(node);
+ IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
+ is = fileStore.openInputStream(EFS.NONE, null);
+ } else {
+ // Use its URL stream.
+ URL url = node.getLocationURL();
+ is = url.openStream();
+ }
+ return Platform.getContentTypeManager().findContentTypeFor(is, node.name);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/FileState.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/FileState.java
new file mode 100644
index 000000000..dbd7a342f
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/FileState.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
+
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.te.runtime.callback.Callback;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.JobExecutor;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpCacheFileDigest;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.CacheState;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * The state object to describe a file's state.
+ */
+public class FileState {
+ /**
+ * The base digest of the file data.
+ */
+ private byte[] base_digest = null;
+
+ /**
+ * The message digest of the file data.
+ */
+ private byte[] target_digest = null;
+
+ /**
+ * The message digest of the local cache data
+ */
+ private byte[] cache_digest = null;
+
+ /**
+ * The cache file's modification time.
+ */
+ private long cache_mtime;
+
+ /**
+ * If the job that computes the local cache's digest is running.
+ */
+ transient boolean cache_digest_running = false;
+
+ /**
+ * If the job that computes the target file's digest is running.
+ */
+ transient boolean target_digest_running = false;
+
+ /**
+ * The file system node whose state is described.
+ */
+ private transient FSTreeNode node;
+
+ /**
+ * Create a file state using the node.
+ *
+ * @param node The file system node.
+ */
+ public FileState(FSTreeNode node) {
+ this.node = node;
+ }
+
+ /**
+ * Create a file state using the specified state data.
+ *
+ * @param mtime The cache file's modification time.
+ * @param cache_digest The cache file's digest.
+ * @param target_digest The target file's digest.
+ * @param base_digest The baseline digest.
+ */
+ public FileState(long mtime, byte[] cache_digest, byte[] target_digest, byte[]base_digest) {
+ this.cache_mtime = mtime;
+ this.cache_digest = cache_digest;
+ this.target_digest = target_digest;
+ this.base_digest = base_digest;
+ }
+
+ /**
+ * Set the file system node.
+ *
+ * @param node The file system node.
+ */
+ void setNode(FSTreeNode node) {
+ this.node = node;
+ }
+
+ /**
+ * Get the node's target file digest.
+ *
+ * @return The target file digest.
+ */
+ public byte[] getTargetDigest() {
+ return target_digest;
+ }
+
+ /**
+ * Get the node's baseline digest.
+ *
+ * @return The baseline digest.
+ */
+ public byte[] getBaseDigest() {
+ return base_digest;
+ }
+
+ /**
+ * Get the node's cache file modification time.
+ *
+ * @return The cache file's modification time.
+ */
+ public long getCacheMTime() {
+ return cache_mtime;
+ }
+
+ /**
+ * Get the node's cache file digest.
+ *
+ * @return The cache file digest.
+ */
+ public byte[] getCacheDigest() {
+ return cache_digest;
+ }
+
+ /**
+ * Update the cache state of this file and invoke callback once the update is done.
+ * If the callback is null, then do not invoke any callback.
+ *
+ * @param callback Callback invoked after updating.
+ */
+ public synchronized void updateState(final ICallback callback) {
+ File file = CacheManager.getCacheFile(node);
+ if (file.exists()) {
+ long cache_mtime = file.lastModified();
+ if (!cache_digest_running && (cache_digest == null || this.cache_mtime != cache_mtime)) {
+ cache_digest_running = true;
+ this.cache_mtime = cache_mtime;
+ final OpCacheFileDigest op = new OpCacheFileDigest(node);
+ new JobExecutor(new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ if (status.isOK()) {
+ updateCacheDigest(op.getDigest());
+ }
+ cache_digest_running = false;
+ if (status.isOK()) {
+ updateState(callback);
+ }
+ else if(callback != null){
+ callback.done(this, status);
+ }
+ }
+ }).execute(op);
+ }
+ else if (!target_digest_running && target_digest == null) {
+ target_digest_running = true;
+ node.refresh(new Callback(){
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ target_digest_running = false;
+ if (status.isOK()) {
+ updateState(callback);
+ }
+ else if(callback != null){
+ callback.done(this, status);
+ }
+ }
+ });
+ }
+ else if (callback != null) callback.done(this, Status.OK_STATUS);
+ }
+ else if (callback != null) callback.done(this, Status.OK_STATUS);
+ }
+
+ /**
+ * Get this node's cache state using the current state data.
+ *
+ * @return The state expressed in a CacheState enum value.
+ */
+ public synchronized CacheState getCacheState() {
+ File file = CacheManager.getCacheFile(node);
+ if (!file.exists()) return CacheState.consistent;
+ updateState(null);
+ if (cache_digest == null || target_digest == null) return CacheState.consistent;
+ if(isUnchanged(target_digest, cache_digest)) {
+ base_digest = target_digest;
+ return CacheState.consistent;
+ }
+ if(isUnchanged(base_digest, cache_digest)){
+ return CacheState.outdated;
+ }
+ if (isUnchanged(target_digest, base_digest)) {
+ return CacheState.modified;
+ }
+ return CacheState.conflict;
+ }
+
+ /**
+ * Update the node's target digest and fire an event.
+ *
+ * @param target_digest The new target digest data.
+ */
+ public void updateTargetDigest(byte[] target_digest) {
+ this.target_digest = target_digest;
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "target_digest", null, target_digest); //$NON-NLS-1$
+ node.firePropertyChange(event);
+ }
+
+ /**
+ * Compare the two digests to see if they are equal to each other.
+ *
+ * @param digest1 The first digest.
+ * @param digest2 The second digest.
+ * @return true if they are equal.
+ */
+ private boolean isUnchanged(byte[] digest1, byte[] digest2){
+ if(digest1 != null && digest2 != null && digest1.length == digest2.length) {
+ for (int i = 0; i < digest1.length; i++) {
+ if(digest1[i] != digest2[i]) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Update the cache file digest data and fire an event.
+ *
+ * @param cache_digest The new cache file digest data.
+ */
+ public void updateCacheDigest(byte[] cache_digest) {
+ byte[] old_digest = cache_digest;
+ this.cache_digest = cache_digest;
+ PropertyChangeEvent event = new PropertyChangeEvent(node, "cache_digest", old_digest, cache_digest); //$NON-NLS-1$
+ node.firePropertyChange(event);
+ }
+
+ /**
+ * Reset all of the node's digest data to a new digest data.
+ *
+ * @param digest The new digest data.
+ */
+ public void reset(byte[] digest) {
+ cache_digest = digest;
+ target_digest = digest;
+ base_digest = digest;
+ PropertyChangeEvent event = new PropertyChangeEvent(node, "reset_digest", null, digest); //$NON-NLS-1$
+ node.firePropertyChange(event);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManager.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManager.java
new file mode 100644
index 000000000..e28abd4a3
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManager.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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:
+ * William Chen (Wind River) [360494]Provide an "Open With" action in the pop
+ * up menu of file system nodes of Target Explorer.
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.tcf.te.runtime.persistence.interfaces.IURIPersistenceService;
+import org.eclipse.tcf.te.runtime.services.ServiceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode;
+
+/**
+ * A facility class to load and save persistent data such including resolved content types, file's
+ * properties, and time stamps etc.
+ */
+public class PersistenceManager {
+ // The singleton instance.
+ private static volatile PersistenceManager instance;
+
+ // The time stamp for each file.
+ Map<URI, FileState> digests;
+
+ // Already known resolved content type of file nodes specified by their URIs.
+ Map<URI, IContentType> resolved;
+
+ // Already known unresolvable file nodes specified by their URIs.
+ Map<URI, URI> unresolved;
+
+ // The persistent properties of the files.
+ Map<URI, Map<QualifiedName, String>> properties;
+
+ // The file used to store persistent properties of each file.
+ private static final String PERSISTENT_FILE = "persistent.ini"; //$NON-NLS-1$
+
+ /**
+ * Get the singleton cache manager.
+ *
+ * @return The singleton cache manager.
+ */
+ public static PersistenceManager getInstance() {
+ if (instance == null) {
+ instance = new PersistenceManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Create a Persistent Manager instance.
+ */
+ private PersistenceManager() {
+ digests = new HashMap<URI, FileState>();
+ resolved = new HashMap<URI, IContentType>();
+ unresolved = new HashMap<URI, URI>();
+ properties = new HashMap<URI, Map<QualifiedName, String>>();
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ @Override
+ public void run() throws Exception {
+ IURIPersistenceService service = ServiceManager.getInstance().getService(IURIPersistenceService.class);
+ File location = CacheManager.getCacheRoot();
+ File persistentFile = new File(location, PERSISTENT_FILE);
+ if (persistentFile.exists()) {
+ service.read(PersistenceManager.this, persistentFile.getAbsoluteFile().toURI());
+ }
+ }});
+ }
+
+ /**
+ * If the node is already considered unresolvable.
+ *
+ * @param node The file node.
+ * @return true if it is not resolvable or else false.
+ */
+ public boolean isUnresovled(FSTreeNode node) {
+ return unresolved.get(node.getLocationURI()) != null;
+ }
+
+ /**
+ * Get the resolved content type of the node.
+ *
+ * @param node The file node.
+ * @return the content type of the node if it is resolvable or null.
+ */
+ public IContentType getResolved(FSTreeNode node) {
+ return resolved.get(node.getLocationURI());
+ }
+
+ /**
+ * Add the node and its content type to the resolved list.
+ *
+ * @param node The file node.
+ * @param contentType Its content type.
+ */
+ public void addResovled(FSTreeNode node, IContentType contentType) {
+ resolved.put(node.getLocationURI(), contentType);
+ }
+
+ /**
+ * Add the node as an unresolvable node.
+ *
+ * @param node The file node.
+ */
+ public void addUnresolved(FSTreeNode node) {
+ unresolved.put(node.getLocationURI(), node.getLocationURI());
+ }
+
+ /**
+ * Set the time stamp of the FSTreeNode with the specified location.
+ *
+ * @param uri The FSTreeNode's location URI.
+ * @param digest The new base time stamp to be set.
+ */
+ public void setFileDigest(URI uri, FileState digest) {
+ digests.put(uri, digest);
+ }
+
+ /**
+ * Remove the time stamp entry with the specified URI.
+ *
+ * @param uri The URI key.
+ */
+ public void removeFileDigest(URI uri) {
+ digests.remove(uri);
+ }
+
+ /**
+ * Get the time stamp of the FSTreeNode with the specified location.
+ *
+ * @param uri The FSTreeNode's location URI.
+ * @return The FSTreeNode's base time stamp.
+ */
+ public FileState getFileDigest(FSTreeNode node) {
+ URI uri = node.getLocationURI();
+ FileState digest = digests.get(uri);
+ if(digest == null) {
+ digest = new FileState(node);
+ digests.put(uri, digest);
+ }
+ digest.setNode(node);
+ return digest;
+ }
+
+ /**
+ * Get the file properties of the specified node from the properties map.
+ *
+ * @param node The file node.
+ * @return The file properties object or empty properties object if it does not exist.
+ */
+ public Map<QualifiedName, String> getPersistentProperties(FSTreeNode node) {
+ Map<QualifiedName, String> nodeProperties = properties.get(node.getLocationURI());
+ if (nodeProperties == null) {
+ nodeProperties = Collections.synchronizedMap(new HashMap<QualifiedName, String>());
+ properties.put(node.getLocationURI(), nodeProperties);
+ }
+ return nodeProperties;
+ }
+
+ /**
+ * Dispose the cache manager so that it has a chance to save the digests and the persistent
+ * properties.
+ */
+ public void dispose() {
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose.
+ }
+ @Override
+ public void run() throws Exception {
+ IURIPersistenceService service = ServiceManager.getInstance().getService(IURIPersistenceService.class);
+ File location = CacheManager.getCacheRoot();
+ File persistentFile = new File(location, PERSISTENT_FILE);
+ service.write(PersistenceManager.this, persistentFile.getAbsoluteFile().toURI());
+ }});
+ }
+
+ /**
+ * Returns if or if not the persistence manager needs to be disposed.
+ *
+ * @return <code>True</code> if the persistence manager needs disposal, <code>false</code> otherwise.
+ */
+ public final static boolean needsDisposal() {
+ return instance != null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManagerDelegate.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManagerDelegate.java
new file mode 100644
index 000000000..28fd5baa1
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/utils/PersistenceManagerDelegate.java
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.tcf.te.runtime.persistence.delegates.GsonMapPersistenceDelegate;
+
+/**
+ * The persistence delegate to persist or restore a map whose keys are URIs.
+ */
+public class PersistenceManagerDelegate extends GsonMapPersistenceDelegate {
+
+ private static final String MAP_KEY_MTIME = "mtime"; //$NON-NLS-1$
+ private static final String MAP_KEY_TARGET = "target"; //$NON-NLS-1$
+ private static final String MAP_KEY_CACHE = "cache"; //$NON-NLS-1$
+ private static final String MAP_KEY_BASE = "base"; //$NON-NLS-1$
+ private static final String MAP_KEY_UNRESOLVED = "unresolved"; //$NON-NLS-1$
+ private static final String MAP_KEY_RESOLVED = "resolved"; //$NON-NLS-1$
+ private static final String MAP_KEY_PROPERTIES = "properties"; //$NON-NLS-1$
+ private static final String MAP_KEY_DIGESTS = "digests"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.tcf.te.runtime.persistence.interfaces.IPersistenceDelegate#getPersistedClass(
+ * java.lang.Object)
+ */
+ @Override
+ public Class<?> getPersistedClass(Object context) {
+ return PersistenceManager.class;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.tcf.te.runtime.persistence.AbstractGsonMapPersistenceDelegate#toMap(java.lang
+ * .Object)
+ */
+ @Override
+ protected Map<String, Object> toMap(Object context) throws IOException {
+ PersistenceManager pMgr = (PersistenceManager) context;
+ Map<String, Object> result = new HashMap<String, Object>();
+ result.put(MAP_KEY_DIGESTS, persistDigests(pMgr.digests));
+ result.put(MAP_KEY_PROPERTIES, persistProperties(pMgr.properties));
+ result.put(MAP_KEY_RESOLVED, persistResolved(pMgr.resolved));
+ result.put(MAP_KEY_UNRESOLVED, persistUnresolved(pMgr.unresolved));
+ return result;
+ }
+
+ private Object persistProperties(Map<URI, Map<QualifiedName, String>> properties) {
+ Map<String, Object> result = new HashMap<String, Object>();
+ if (properties != null) {
+ for (Entry<URI, Map<QualifiedName, String>> entry : properties.entrySet()) {
+ Map<QualifiedName, String> map = entry.getValue();
+ Map<String, Object> valueMap = qNames2Map(map);
+ result.put(entry.getKey().toString(), valueMap);
+ }
+ }
+ return result;
+ }
+
+ private Object persistResolved(Map<URI, IContentType> resolved) {
+ Map<String, Object> result = new HashMap<String, Object>();
+ if (resolved != null) {
+ for (Entry<URI, IContentType> entry : resolved.entrySet()) {
+ IContentType object = entry.getValue();
+ String value = object.getId();
+ result.put(entry.getKey().toString(), value);
+ }
+ }
+ return result;
+ }
+
+ private Object persistUnresolved(Map<URI, URI> unresolved) {
+ Map<String, Object> result = new HashMap<String, Object>();
+ if (unresolved != null) {
+ for (Entry<URI, URI> entry : unresolved.entrySet()) {
+ URI uri = entry.getValue();
+ String value = uri.toString();
+ result.put(entry.getKey().toString(), value);
+ }
+ }
+ return result;
+ }
+
+ private Object persistDigests(Map<URI, FileState> states) {
+ Map<String, Object> result = new HashMap<String, Object>();
+ if (states != null) {
+ for (Entry<URI, FileState> entry : states.entrySet()) {
+ FileState fileState = entry.getValue();
+ Map<String, Object> value = digest2map(fileState);
+ result.put(entry.getKey().toString(), value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Translate the specified map whose keys are QualifiedNames to a map whose keys are strings.
+ *
+ * @param map The map to be translated.
+ * @return a map with string keys.
+ */
+ private Map<String, Object> qNames2Map(Map<QualifiedName, String> map) {
+ Map<String, Object> result = new HashMap<String, Object>();
+ for (Entry<QualifiedName, String> entry : map.entrySet()) {
+ result.put(entry.getKey().toString(), entry.getValue());
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.tcf.te.runtime.persistence.AbstractGsonMapPersistenceDelegate#fromMap(java.util
+ * .Map, java.lang.Object)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object fromMap(Map<String, Object> map, Object context) throws IOException {
+ PersistenceManager result = (PersistenceManager) context;
+ Map<String, Map<String, Object>> digests = (Map<String, Map<String, Object>>) map.get(MAP_KEY_DIGESTS);
+ Map<String, Map<String, String>> properties = (Map<String, Map<String, String>>) map.get(MAP_KEY_PROPERTIES);
+ Map<String, String> resolved = (Map<String, String>) map.get(MAP_KEY_RESOLVED);
+ Map<String, String> unresolved = (Map<String, String>) map.get(MAP_KEY_UNRESOLVED);
+ restoreDigests(digests, result.digests);
+ restoreProperites(properties, result.properties);
+ restoreResolved(resolved, result.resolved);
+ restoreUnresolved(unresolved, result.unresolved);
+ return result;
+ }
+
+ private void restoreUnresolved(Map<String, String> map, Map<URI, URI> unresolved) {
+ for (Entry<String, String> entry : map.entrySet()) {
+ String value = entry.getValue();
+ URI uri = toURI(entry.getKey());
+ Assert.isNotNull(uri);
+ uri = toURI(value);
+ Assert.isNotNull(uri);
+ unresolved.put(uri, uri);
+ }
+ }
+
+ private void restoreResolved(Map<String, String> map, Map<URI, IContentType> contentTypes) {
+ for (Entry<String, String> entry : map.entrySet()) {
+ String value = entry.getValue();
+ URI uri = toURI(entry.getKey());
+ Assert.isNotNull(uri);
+ IContentType contentType = Platform.getContentTypeManager().getContentType(value);
+ contentTypes.put(uri, contentType);
+ }
+ }
+
+ private void restoreDigests(Map<String, Map<String, Object>> map, Map<URI, FileState> states) {
+ for (Entry<String, Map<String, Object>> entry : map.entrySet()) {
+ Map<String, Object> value = entry.getValue();
+ URI uri = toURI(entry.getKey());
+ Assert.isNotNull(uri);
+ FileState digest = map2digest(value);
+ states.put(uri, digest);
+ }
+ }
+
+ private void restoreProperites(Map<String, Map<String, String>> map, Map<URI, Map<QualifiedName, String>> properties) {
+ for (Entry<String, Map<String, String>> entry : map.entrySet()) {
+ Map<String, String> value = entry.getValue();
+ URI uri = toURI(entry.getKey());
+ Assert.isNotNull(uri);
+ Map<QualifiedName, String> valueMap = toQNameMap(value);
+ properties.put(uri, valueMap);
+ }
+ }
+
+ private FileState map2digest(Map<String, Object> value) {
+ byte[] base_digest = string2digest((String) value.get(MAP_KEY_BASE));
+ byte[] cache_digest = string2digest((String) value.get(MAP_KEY_CACHE));
+ byte[] target_digest = string2digest((String) value.get(MAP_KEY_TARGET));
+ Number number = (Number) value.get(MAP_KEY_MTIME);
+ long mtime = number.longValue();
+ return new FileState(mtime, cache_digest, target_digest, base_digest);
+ }
+
+ private Map<String, Object> digest2map(FileState digest) {
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put(MAP_KEY_BASE, digest2string(digest.getBaseDigest()));
+ map.put(MAP_KEY_CACHE, digest2string(digest.getCacheDigest()));
+ map.put(MAP_KEY_TARGET, digest2string(digest.getTargetDigest()));
+ map.put(MAP_KEY_MTIME, Long.valueOf(digest.getCacheMTime()));
+ return map;
+ }
+
+ private String digest2string(byte[] digest) {
+ if (digest != null && digest.length > 0) {
+ StringBuilder buffer = new StringBuilder();
+ for (byte element : digest) {
+ int d = element & 0xff;
+ String sByte = Integer.toHexString(d);
+ while (sByte.length() < 2)
+ {
+ sByte = "0" + sByte; //$NON-NLS-1$
+ }
+ buffer.append(sByte.toLowerCase());
+ }
+ return buffer.toString();
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ private byte[] string2digest(String string) {
+ if (string != null && string.length() > 0) {
+ int count = string.length() / 2;
+ byte[] digest = new byte[count];
+ for (int i = 0; i < count; i++) {
+ try {
+ String seg = string.substring(2 * i, 2 * (i + 1));
+ int d = Integer.parseInt(seg, 16);
+ digest[i] = (byte) d;
+ }
+ catch (Exception e) {
+ }
+ }
+ return digest;
+ }
+ return new byte[0];
+ }
+
+ /**
+ * Translate the specified map with string keys to a map whose keys are qualified names.
+ *
+ * @param strMap The map with string keys.
+ * @return A map with qualified names as keys.
+ */
+ private Map<QualifiedName, String> toQNameMap(Map<String, String> strMap) {
+ Map<QualifiedName, String> result = new HashMap<QualifiedName, String>();
+ for (Entry<String, String> entry : strMap.entrySet()) {
+ int dot = entry.getKey().lastIndexOf(":"); //$NON-NLS-1$
+ String qualifier = null;
+ String local = entry.getKey();
+ if (dot != -1) {
+ qualifier = entry.getKey().substring(0, dot);
+ local = entry.getKey().substring(dot + 1);
+ }
+ QualifiedName name = new QualifiedName(qualifier, local);
+ result.put(name, strMap.get(entry.getKey()));
+ }
+ return result;
+ }
+
+ /**
+ * Convert the string to a URI.
+ *
+ * @param string The string to be converted.
+ * @return the URI or null if there're issues when parsing.
+ */
+ private URI toURI(final String string) {
+ final AtomicReference<URI> ref = new AtomicReference<URI>();
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose.
+ }
+
+ @Override
+ public void run() throws Exception {
+ ref.set(new URI(string));
+ }
+ });
+ return ref.get();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/AbstractTreeNode.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/AbstractTreeNode.java
new file mode 100644
index 000000000..103ef6293
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/AbstractTreeNode.java
@@ -0,0 +1,342 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.model;
+
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.tcf.te.core.interfaces.IPropertyChangeProvider;
+import org.eclipse.tcf.te.core.interfaces.IViewerInput;
+import org.eclipse.tcf.te.runtime.callback.Callback;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager;
+import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.NullOpExecutor;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpUser;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+
+/**
+ * The base class of FSTreeNode and ProcessTreeNode which provides base members and methods.
+ */
+public abstract class AbstractTreeNode extends PlatformObject {
+ // The unique id of this node.
+ protected final UUID uniqueId = UUID.randomUUID();
+
+ /**
+ * The tree node name.
+ */
+ public String name = null;
+
+ /**
+ * The tree node type.
+ */
+ public String type = null;
+
+ /**
+ * The peer node the file system tree node is associated with.
+ */
+ public IPeerNode peerNode = null;
+
+ /**
+ * Flag to mark once the children of the node got queried
+ */
+ public boolean childrenQueried = false;
+
+ /**
+ * Flag to mark once the children query is running
+ */
+ public boolean childrenQueryRunning = false;
+
+ /**
+ * The tree node parent.
+ */
+ protected AbstractTreeNode parent = null;
+
+ /**
+ * The tree node children.
+ */
+ protected List<AbstractTreeNode> children = Collections.synchronizedList(new ArrayList<AbstractTreeNode>());
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public final int hashCode() {
+ return uniqueId.hashCode();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public final boolean equals(Object obj) {
+ if(this == obj)
+ return true;
+ if (obj instanceof AbstractTreeNode) {
+ return uniqueId.equals(((AbstractTreeNode) obj).uniqueId);
+ }
+ return super.equals(obj);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder(getClass().getSimpleName());
+ buffer.append(": name=" + (name != null ? name : super.toString())); //$NON-NLS-1$
+ buffer.append(", UUID=" + uniqueId.toString()); //$NON-NLS-1$
+ return buffer.toString();
+ }
+
+ /**
+ * Called when the children query is done.
+ */
+ public void queryDone() {
+ childrenQueryRunning = false;
+ childrenQueried = true;
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "query_done", Boolean.FALSE, Boolean.TRUE); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Called when the children query is started.
+ */
+ public void queryStarted() {
+ childrenQueryRunning = true;
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "query_started", Boolean.FALSE, Boolean.TRUE); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Get the user account of the specified TCF peer.
+ *
+ * @param peerNode The peer node of the TCF agent.
+ * @return The user account that runs the agent.
+ */
+ protected UserAccount getUserAccount(IPeerNode peerNode) {
+ OpUser user = new OpUser(peerNode);
+ new NullOpExecutor().execute(user);
+ return user.getUserAccount();
+ }
+
+ /**
+ * Fire a property change event to notify one of the node's property has changed.
+ *
+ * @param event The property change event.
+ */
+ public void firePropertyChange(PropertyChangeEvent event) {
+ if(peerNode != null) {
+ IPropertyChangeProvider provider = (IPropertyChangeProvider) peerNode.getAdapter(IPropertyChangeProvider.class);
+ provider.firePropertyChange(event);
+ } else if(parent != null) {
+ parent.firePropertyChange(event);
+ }
+ }
+
+ /**
+ * Add the specified nodes to the children list.
+ *
+ * @param nodes The nodes to be added.
+ */
+ public void addChidren(List<? extends AbstractTreeNode> nodes) {
+ children.addAll(nodes);
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "addChildren", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Remove the specified nodes from the children list.
+ *
+ * @param nodes The nodes to be removed.
+ */
+ public void removeChildren(List<? extends AbstractTreeNode> nodes) {
+ children.removeAll(nodes);
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "removeChildren", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Add the specified the node to the children list.
+ *
+ * @param node The child node to be added.
+ */
+ public void addChild(AbstractTreeNode node) {
+ children.add(node);
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "addChild", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Remove the specified child node from its children list.
+ *
+ * @param node The child node to be removed.
+ */
+ public void removeChild(AbstractTreeNode node) {
+ children.remove(node);
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "removeChild", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * Clear the children of this folder.
+ */
+ public void clearChildren() {
+ children.clear();
+ PropertyChangeEvent event = new PropertyChangeEvent(this, "clearChildren", null, null); //$NON-NLS-1$
+ firePropertyChange(event);
+ }
+
+ /**
+ * If this node is ancestor of the specified node.
+ * @return true if it is.
+ */
+ public boolean isAncestorOf(AbstractTreeNode node) {
+ if (node == null) return false;
+ if (node.parent == this) return true;
+ return isAncestorOf(node.parent);
+ }
+
+ /**
+ * Get the parent node of this node.
+ *
+ * @return The parent node.
+ */
+ public AbstractTreeNode getParent() {
+ return parent;
+ }
+
+ /**
+ * Set the parent node of this node.
+ *
+ * @param parent The parent node.
+ */
+ public void setParent(AbstractTreeNode parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Recursively refresh the children of the given process context.
+ *
+ * @param parentNode The process context node. Must not be <code>null</code>.
+ */
+ public void refresh() {
+ refresh(null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if(IViewerInput.class.equals(adapter)) {
+ return peerNode.getAdapter(IViewerInput.class);
+ }
+ if(IPropertyChangeProvider.class.equals(adapter)) {
+ return peerNode.getAdapter(adapter);
+ }
+ return super.getAdapter(adapter);
+ }
+
+ /**
+ * Recursively refresh the children of the given process context with a callback, which is
+ * called when whole process is finished.
+ *
+ * @param callback The callback object, or <code>null</code> when callback is not needed.
+ */
+ public void refresh(final ICallback callback) {
+ Map<String, Boolean> flags = new HashMap<String, Boolean>();
+ flags.put(IChannelManager.FLAG_NO_PATH_MAP, Boolean.TRUE);
+ Tcf.getChannelManager().openChannel(peerNode.getPeer(), flags, doCreateRefreshDoneOpenChannel(new Callback() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.callback.Callback#internalDone(java.lang.Object, org.eclipse.core.runtime.IStatus)
+ */
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ refreshChildren(callback);
+ }
+ }));
+ }
+
+ /**
+ * Create the callback object of opening channel for refreshing itself.
+ *
+ * @param callback The callback object.
+ * @return The callback object.
+ */
+ protected abstract DoneOpenChannel doCreateRefreshDoneOpenChannel(ICallback callback);
+
+ /**
+ * Query the children of this file system node.
+ */
+ public final void queryChildren() {
+ queryChildren(null);
+ }
+ /**
+ * Query the children of this file system node.
+ */
+ public void queryChildren(ICallback callback) {
+ queryStarted();
+ Map<String, Boolean> flags = new HashMap<String, Boolean>();
+ flags.put(IChannelManager.FLAG_NO_PATH_MAP, Boolean.TRUE);
+ Tcf.getChannelManager().openChannel(peerNode.getPeer(), flags, doCreateQueryDoneOpenChannel(callback));
+ }
+
+ /**
+ * Create the callback object of opening channel for querying children.
+ *
+ * @return The callback object.
+ */
+ protected abstract DoneOpenChannel doCreateQueryDoneOpenChannel(ICallback callback);
+
+ /**
+ * Return if this node is the system root.
+ *
+ * @return true if it is.
+ */
+ public abstract boolean isSystemRoot();
+
+ /**
+ * Get the children of this tree node.
+ *
+ * @return The list of the children.
+ */
+ public List<? extends AbstractTreeNode> getChildren() {
+ return new ArrayList<AbstractTreeNode>(children);
+ }
+
+ /**
+ * Refresh the children's children.
+ */
+ public final void refreshChildren() {
+ refreshChildren(null);
+ }
+
+ /**
+ * Refresh the children's children.
+ */
+ public abstract void refreshChildren(ICallback callback);
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/CacheState.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/CacheState.java
new file mode 100644
index 000000000..7d24a187c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/CacheState.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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.core.model;
+
+/**
+ * The enumeration that defines the states of a file's local cache, including "consistent", "modified",
+ * "outdated" and "conflict".
+ */
+public enum CacheState {
+ consistent, // Neither of the local file and the remote file has been changed since checking out.
+ modified, // The local file has changed while the remote file has not since checking out.
+ outdated, // The remote file has changed while the local file has not since checking out.
+ conflict // Both the local file and the remote file have changed since checking out.
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/FSTreeNode.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/FSTreeNode.java
new file mode 100644
index 000000000..9242a18cb
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/FSTreeNode.java
@@ -0,0 +1,681 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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) - [345384] Provide property pages for remote file system nodes
+ * William Chen (Wind River) - [352302]Opening a file in an editor depending on
+ * the client's permissions.
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.model;
+
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.DirEntry;
+import org.eclipse.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tcf.te.core.interfaces.IFilterable;
+import org.eclipse.tcf.te.runtime.callback.Callback;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.tcf.core.concurrent.CallbackMonitor;
+import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IWindowsFileAttributes;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks.QueryDoneOpenChannel;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.callbacks.RefreshStateDoneOpenChannel;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpTargetFileDigest;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.testers.TargetPropertyTester;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLConnection;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLStreamHandlerService;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.FileState;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+
+/**
+ * Representation of a file system tree node.
+ * <p>
+ * <b>Note:</b> Node construction and child list access is limited to the TCF
+ * event dispatch thread.
+ */
+public final class FSTreeNode extends AbstractTreeNode implements Cloneable, IFilterable {
+ // The constant to access the Windows Attributes.
+ private static final String KEY_WIN32_ATTRS = "Win32Attrs"; //$NON-NLS-1$
+
+ /**
+ * The tree node file system attributes
+ */
+ public IFileSystem.FileAttrs attr = null;
+
+ /**
+ * Create a folder node using the specified parent node, the directory entry
+ * and the flag to indicate if it is a root node.
+ *
+ * @param parentNode The parent node.
+ * @param entry The directory entry.
+ * @param entryIsRootNode If this folder is root folder.
+ */
+ public FSTreeNode(FSTreeNode parentNode, DirEntry entry, boolean entryIsRootNode) {
+ Assert.isNotNull(entry);
+ IFileSystem.FileAttrs attrs = entry.attrs;
+
+ this.attr = attrs;
+ this.name = entry.filename;
+ if (attrs == null || attrs.isDirectory()) {
+ this.type = entryIsRootNode ? "FSRootDirNode" : "FSDirNode"; //$NON-NLS-1$ //$NON-NLS-2$
+ } else if (attrs.isFile()) {
+ this.type = "FSFileNode"; //$NON-NLS-1$
+ }
+ this.parent = parentNode;
+ this.peerNode = parentNode.peerNode;
+ Assert.isTrue(Protocol.isDispatchThread());
+ }
+
+ /**
+ * Constructor.
+ */
+ public FSTreeNode() {
+ Assert.isTrue(Protocol.isDispatchThread());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone() {
+ if (Protocol.isDispatchThread()) {
+ FSTreeNode clone = new FSTreeNode();
+ clone.childrenQueried = childrenQueried;
+ clone.childrenQueryRunning = childrenQueryRunning;
+ clone.name = name;
+ clone.parent = parent;
+ clone.peerNode = peerNode;
+ clone.type = type;
+ if (attr != null) {
+ Map<String, Object> attributes = new HashMap<String, Object>(attr.attributes);
+ clone.attr = new IFileSystem.FileAttrs(attr.flags, attr.size, attr.uid, attr.gid, attr.permissions, attr.atime, attr.mtime, attributes);
+ } else {
+ clone.attr = null;
+ }
+ return clone;
+ }
+ final Object[] objects = new Object[1];
+ Protocol.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ objects[0] = FSTreeNode.this.clone();
+ }
+ });
+ return objects[0];
+ }
+
+ /**
+ * Change the file/folder's write permission.
+ * @param b true if the agent is granted with its write permission.
+ */
+ public void setWritable(boolean b) {
+ UserAccount account = getUserAccount(peerNode);
+ if (account != null && attr != null) {
+ int bit;
+ if (attr.uid == account.getEUID()) {
+ bit = IFileSystem.S_IWUSR;
+ } else if (attr.gid == account.getEGID()) {
+ bit = IFileSystem.S_IWGRP;
+ } else {
+ bit = IFileSystem.S_IWOTH;
+ }
+ int permissions = attr.permissions;
+ setPermissions(b ? (permissions | bit):(permissions & ~ bit));
+ }
+ }
+
+ /**
+ * Set the file's permissions.
+ * @param permissions The new permissions.
+ */
+ public void setPermissions(int permissions) {
+ attr = new IFileSystem.FileAttrs(attr.flags, attr.size, attr.uid, attr.gid, permissions, attr.atime, attr.mtime, attr.attributes);
+ }
+
+ /**
+ * Returns the children outside of TCF thread.
+ *
+ * @return The children list.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<FSTreeNode> getChildren() {
+ return (List<FSTreeNode>) super.getChildren();
+ }
+
+ /**
+ * Return if the node is a Windows file/folder node.
+ *
+ * @return true if it is a Windows node, or else false.
+ */
+ public boolean isWindowsNode() {
+ if (attr != null && attr.attributes != null) {
+ return attr.attributes.containsKey(KEY_WIN32_ATTRS);
+ }
+ if (peerNode != null) {
+ String OSName = TargetPropertyTester.getOSName(peerNode);
+ if(OSName != null){
+ return OSName.startsWith("Windows"); //$NON-NLS-1$
+ }
+ }
+ synchronized (children) {
+ if (!children.isEmpty()) {
+ for (AbstractTreeNode treeNode : children) {
+ FSTreeNode node = (FSTreeNode) treeNode;
+ if(node.hasOSInfo()) {
+ return node.isWindowsNode();
+ }
+ }
+ }
+ }
+ if(parent != null) {
+ return ((FSTreeNode)parent).isWindowsNode();
+ }
+ return false;
+ }
+
+ /**
+ * If this node has OS information.
+ *
+ * @return true if it has.
+ */
+ private boolean hasOSInfo() {
+ return attr != null && attr.attributes != null ||
+ peerNode != null && TargetPropertyTester.getOSName(peerNode)!= null;
+ }
+
+ /**
+ * Return if the node is a file.
+ *
+ * @return true if it is a file, or else false.
+ */
+ public boolean isFile() {
+ if (attr != null) {
+ return attr.isFile();
+ }
+ if (type != null)
+ {
+ return type.equals("FSFileNode"); //$NON-NLS-1$
+ }
+ return false;
+ }
+
+ /**
+ * Return if the node is a directory.
+ *
+ * @return true if it is a directory, or else false.
+ */
+ public boolean isDirectory() {
+ if (attr != null) {
+ return attr.isDirectory();
+ }
+ if (type != null)
+ {
+ return type.endsWith("DirNode"); //$NON-NLS-1$
+ }
+ return false;
+ }
+
+ /**
+ * Return if the attribute specified by the mask bit is turned on.
+ *
+ * @param bit
+ * The attribute's mask bit.
+ * @return true if it is on, or else false.
+ */
+ public boolean isWin32AttrOn(int bit) {
+ if (attr != null && attr.attributes.get(KEY_WIN32_ATTRS) instanceof Integer) {
+ Integer win32Attrs = (Integer) attr.attributes.get(KEY_WIN32_ATTRS);
+ return (win32Attrs.intValue() & bit) != 0;
+ }
+ return false;
+ }
+
+ /**
+ * Set the attribute specified by the mask bit to on or off.
+ * @param bit The attribute's mask bit.
+ * @param on The flag if the bit should be turned on or off.
+ */
+ public void setWin32Attr(int bit, boolean on) {
+ if (attr != null && attr.attributes.get(KEY_WIN32_ATTRS) instanceof Integer) {
+ int win32attr = ((Integer) attr.attributes.get(KEY_WIN32_ATTRS)).intValue();
+ win32attr = on ? (win32attr | bit) : (win32attr & ~bit);
+ attr.attributes.put(KEY_WIN32_ATTRS, Integer.valueOf(win32attr));
+ }
+ }
+
+ /**
+ * Get the file's win32 attributes.
+ * @return The file's win32 attributes.
+ */
+ public int getWin32Attrs() {
+ if (attr != null && attr.attributes.get(KEY_WIN32_ATTRS) instanceof Integer) {
+ return ((Integer) attr.attributes.get(KEY_WIN32_ATTRS)).intValue();
+ }
+ return 0;
+ }
+
+ /**
+ * Return if this file/folder is hidden.
+ *
+ * @return true if it is hidden, or else false.
+ */
+ public boolean isHidden() {
+ return isWin32AttrOn(IWindowsFileAttributes.FILE_ATTRIBUTE_HIDDEN);
+ }
+
+ /**
+ * Set the file/folder hidden attribute's value.
+ * @param hidden The new value.
+ */
+ public void setHidden(boolean hidden) {
+ setWin32Attr(IWindowsFileAttributes.FILE_ATTRIBUTE_HIDDEN, hidden);
+ }
+
+ /**
+ * Return if this file/folder is read-only.
+ *
+ * @return true if it is read-only, or else false.
+ */
+ public boolean isReadOnly() {
+ return isWin32AttrOn(IWindowsFileAttributes.FILE_ATTRIBUTE_READONLY);
+ }
+
+ /**
+ * Set the file/folder read-only attribute's value.
+ * @param readOnly The new value.
+ */
+ public void setReadOnly(boolean readOnly) {
+ setWin32Attr(IWindowsFileAttributes.FILE_ATTRIBUTE_READONLY, readOnly);
+ }
+
+ /**
+ * Get the location of a file/folder node using the format of the file
+ * system's platform.
+ *
+ * @param parentNode
+ * The file/folder node.
+ * @return The location of the file/folder.
+ */
+ public String getLocation() {
+ return getLocation(false);
+ }
+
+ /**
+ * Get the location of a file/folder.
+ *
+ * @param cross
+ * If the format is cross-platform.
+ * @return The path to the file/folder.
+ */
+ public String getLocation(boolean cross) {
+ if(isRoot()) {
+ if(cross) {
+ if(isWindowsNode()) {
+ return name.substring(0, name.length() - 1) + "/"; //$NON-NLS-1$
+ }
+ }
+ return name;
+ }
+ String pLoc = getParent().getLocation(cross);
+ if(getParent().isRoot()) {
+ return pLoc + name;
+ }
+ String pathSep = (!cross && isWindowsNode()) ? "\\" : "/"; //$NON-NLS-1$ //$NON-NLS-2$
+ return pLoc + pathSep + name;
+ }
+
+ /**
+ * Get the URL of the file or folder. The URL's format is created in the
+ * following way: tcf:/<TCF_AGENT_ID>/remote/path/to/the/resource... See
+ * {@link TcfURLConnection#TcfURLConnection(URL)}
+ *
+ * @see TcfURLStreamHandlerService#parseURL(URL, String, int, int)
+ * @see #getLocationURI()
+ * @return The URL of the file/folder.
+ */
+ public URL getLocationURL() {
+ try {
+ String id = peerNode.getPeerId();
+ String path = getLocation(true);
+ String location = TcfURLConnection.PROTOCOL_SCHEMA + ":/" + id + (path.startsWith("/") ? path : "/" + path); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return new URL(location);
+ } catch (MalformedURLException e) {
+ assert false;
+ return null;
+ }
+ }
+
+ /**
+ * Get the URI of the file or folder. The URI's format is created in the
+ * following way: tcf:/<TCF_AGENT_ID>/remote/path/to/the/resource...
+ *
+ * @return The URI of the file/folder.
+ */
+ public URI getLocationURI() {
+ try {
+ String id = peerNode.getPeerId();
+ String path = getEncodedURIPath();
+ String location = TcfURLConnection.PROTOCOL_SCHEMA + ":/" + id + (path.startsWith("/") ? path : "/" + path); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return new URI(location);
+ }
+ catch (URISyntaxException e) {
+ assert false;
+ return null;
+ }
+ }
+
+ /**
+ * Encode each segment of the path to a URI compatible name,
+ * and get the URI encoded path.
+ *
+ * @return The encoded path which is URI compatible.
+ */
+ private String getEncodedURIPath() {
+ if(isRoot()) {
+ if(isWindowsNode()) {
+ return name.substring(0, name.length() - 1) + "/"; //$NON-NLS-1$
+ }
+ return name;
+ }
+ final AtomicReference<String> ref = new AtomicReference<String>();
+ SafeRunner.run(new ISafeRunnable(){
+ @Override
+ public void handleException(Throwable exception) {
+ // Ignore on purpose
+ }
+ @Override
+ public void run() throws Exception {
+ ref.set(URLEncoder.encode(name, "UTF-8")); //$NON-NLS-1$
+ }});
+ String segment = ref.get();
+ String pLoc = getParent().getEncodedURIPath();
+ if(getParent().isRoot()) {
+ return pLoc + segment;
+ }
+ return pLoc + "/" + segment; //$NON-NLS-1$
+ }
+
+ /**
+ * If this node is a root node.
+ *
+ * @return true if this node is a root node.
+ */
+ public boolean isRoot() {
+ return type != null && type.equals("FSRootDirNode"); //$NON-NLS-1$
+ }
+
+ /**
+ * If this node is the system root.
+ *
+ * @return true if this node is the system root.
+ */
+ @Override
+ public boolean isSystemRoot() {
+ return type != null && type.equals("FSRootNode"); //$NON-NLS-1$
+ }
+
+ /**
+ * If this file is readable.
+ *
+ * @return true if it is readable.
+ */
+ public boolean isReadable() {
+ UserAccount account = getUserAccount(peerNode);
+ if (account != null && attr != null) {
+ if (attr.uid == account.getEUID()) {
+ return (attr.permissions & IFileSystem.S_IRUSR) != 0;
+ } else if (attr.gid == account.getEGID()) {
+ return (attr.permissions & IFileSystem.S_IRGRP) != 0;
+ } else {
+ return (attr.permissions & IFileSystem.S_IROTH) != 0;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If the agent is the owner of this file/folder.
+ *
+ * @return true if the agent is the owner of this file/folder.
+ */
+ public boolean isAgentOwner() {
+ UserAccount account = getUserAccount(peerNode);
+ if (account != null && attr != null) {
+ return attr.uid == account.getEUID();
+ }
+ return false;
+ }
+
+ /**
+ * If this file is writable.
+ *
+ * @return true if it is writable.
+ */
+ public boolean isWritable() {
+ UserAccount account = getUserAccount(peerNode);
+ if (account != null && attr != null) {
+ if (attr.uid == account.getEUID()) {
+ return (attr.permissions & IFileSystem.S_IWUSR) != 0;
+ } else if (attr.gid == account.getEGID()) {
+ return (attr.permissions & IFileSystem.S_IWGRP) != 0;
+ } else {
+ return (attr.permissions & IFileSystem.S_IWOTH) != 0;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If this file is executable.
+ *
+ * @return true if it is executable.
+ */
+ public boolean isExecutable() {
+ UserAccount account = getUserAccount(peerNode);
+ if (account != null && attr != null) {
+ if (attr.uid == account.getEUID()) {
+ return (attr.permissions & IFileSystem.S_IXUSR) != 0;
+ } else if (attr.gid == account.getEGID()) {
+ return (attr.permissions & IFileSystem.S_IXGRP) != 0;
+ } else {
+ return (attr.permissions & IFileSystem.S_IXOTH) != 0;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test if this file is a windows system file.
+ *
+ * @return true if it is a windows system file.
+ */
+ public boolean isSystem() {
+ return !isRoot() && isWindowsNode() && isWin32AttrOn(IWindowsFileAttributes.FILE_ATTRIBUTE_SYSTEM);
+ }
+
+ /**
+ * Get the type label of the file for displaying purpose.
+ *
+ * @return The type label text.
+ */
+ public String getFileType() {
+ if (isRoot()) {
+ return Messages.FSTreeNode_TypeLocalDisk;
+ }
+ if (isDirectory()) {
+ return Messages.FSTreeNode_TypeFileFolder;
+ }
+ if (isSystem()) {
+ return Messages.FSTreeNode_TypeSystemFile;
+ }
+ IContentType contentType = Platform.getContentTypeManager().findContentTypeFor(name);
+ if (contentType != null) {
+ return contentType.getName();
+ }
+ int lastDot = name.lastIndexOf("."); //$NON-NLS-1$
+ if (lastDot == -1) {
+ return Messages.FSTreeNode_TypeUnknownFile;
+ }
+ return name.substring(lastDot + 1).toUpperCase() + " " + Messages.FSTreeNode_TypeFile; //$NON-NLS-1$
+ }
+
+ /**
+ * Set the file's attributes and trigger property change event.
+ *
+ * @param attrs The new attributes.
+ */
+ public void setAttributes(FileAttrs attrs) {
+ FileAttrs oldAttrs = this.attr;
+ this.attr = attrs;
+ if (attrs != oldAttrs) {
+ firePropertyChange(new PropertyChangeEvent(this, "attributes", oldAttrs, attrs)); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Get the local file's state of the specified tree node. The local file must exist
+ * before calling this method to get its state.
+ *
+ * @param node The tree node whose local file state is going to retrieved.
+ * @return The tree node's latest cache state.
+ */
+ public CacheState getCacheState() {
+ File file = CacheManager.getCacheFile(this);
+ if (!file.exists()) {
+ return CacheState.consistent;
+ }
+ FileState digest = PersistenceManager.getInstance().getFileDigest(this);
+ return digest.getCacheState();
+ }
+
+ /**
+ * Set the file's new name and trigger property change event.
+ *
+ * @param name The new name.
+ */
+ public void setName(String name) {
+ String oldName = this.name;
+ this.name = name;
+ if(name == null && oldName != null || name != null && !name.equals(oldName)) {
+ firePropertyChange(new PropertyChangeEvent(this, "name", oldName, name)); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.model.AbstractTreeNode#doCreateRefreshDoneOpenChannel(org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ protected DoneOpenChannel doCreateRefreshDoneOpenChannel(final ICallback callback) {
+ final FileState digest = PersistenceManager.getInstance().getFileDigest(this);
+ ICallback cb = new Callback(){
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ if (status.isOK() && caller instanceof OpTargetFileDigest) {
+ digest.updateTargetDigest(((OpTargetFileDigest) caller).getDigest());
+ }
+ if (callback != null) {
+ callback.done(caller, status);
+ }
+ }
+ };
+ return new RefreshStateDoneOpenChannel(this, cb);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.model.AbstractTreeNode#doCreateQueryDoneOpenChannel()
+ */
+ @Override
+ protected DoneOpenChannel doCreateQueryDoneOpenChannel(ICallback callback) {
+ return new QueryDoneOpenChannel(this, callback);
+ }
+
+// /**
+// * Override the queryChildren to refresh the second level children upon expanding.
+// */
+// @Override
+// public void queryChildren() {
+// queryChildren(new Callback(){
+// @Override
+// protected void internalDone(Object caller, IStatus status) {
+// refreshChildren();
+// }
+// });
+// }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.model.AbstractTreeNode#getParent()
+ */
+ @Override
+ public FSTreeNode getParent() {
+ return (FSTreeNode) parent;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.filesystem.core.model.AbstractTreeNode#refreshChildren()
+ */
+ @Override
+ public void refreshChildren(ICallback callback) {
+ List<FSTreeNode> children = getChildren();
+ if(!children.isEmpty()) {
+ ICallback proxy = new Callback(callback){
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ queryDone();
+ }
+ };
+ final CallbackMonitor monitor = new CallbackMonitor(proxy);
+ for(FSTreeNode child : children) {
+ if((child.isRoot() || child.isDirectory()) && !child.childrenQueried && !child.childrenQueryRunning) {
+ monitor.lock(child.uniqueId);
+ }
+ }
+ for(FSTreeNode child : children) {
+ if((child.isRoot() || child.isDirectory()) && !child.childrenQueried && !child.childrenQueryRunning) {
+ final UUID uuid = child.uniqueId;
+ child.queryChildren(new Callback(){
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ monitor.unlock(uuid, status);
+ }
+ });
+ }
+ }
+ }
+ else {
+ if (callback != null) callback.done(this, Status.OK_STATUS);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/ModelManager.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/ModelManager.java
new file mode 100644
index 000000000..cc0a5056e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/ModelManager.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime.IRuntimeModel;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+
+/**
+ * Filesystem service model manager implementation.
+ */
+public class ModelManager {
+ // Reference to the runtime models
+ /* default */ static final Map<String, IRuntimeModel> runtimeModels = new HashMap<String, IRuntimeModel>();
+
+ /**
+ * Returns the runtime model instance for the given peer model
+ * <p>
+ * If not yet initialized, a new runtime model will be initialized before returning.
+ *
+ * @param peerNode The peer model instance. Must not be <code>null</code>.
+ * @return The runtime model.
+ */
+ public static IRuntimeModel getRuntimeModel(final IPeerNode peerNode) {
+ Assert.isNotNull(peerNode);
+
+ // The result reference holder
+ final AtomicReference<IRuntimeModel> runtimeModel = new AtomicReference<IRuntimeModel>();
+
+ // Create the runnable to execute
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ // Get the peer id
+ String id = peerNode.getPeerId();
+ // Lookup the runtime model instance
+ IRuntimeModel candidate = runtimeModels.get(id);
+ // Initialize a new runtime model instance if necessary
+ if (candidate == null) {
+ candidate = initializeRuntimeModel(peerNode);
+ if (candidate != null) runtimeModels.put(id, candidate);
+ }
+ // Store to the result reference holder
+ runtimeModel.set(candidate);
+ }
+ };
+
+ // Execute the runnable
+ if (Protocol.isDispatchThread()) runnable.run();
+ else Protocol.invokeAndWait(runnable);
+
+ return runtimeModel.get();
+ }
+
+ /**
+ * Initialize the runtime model.
+ * <p>
+ * Must be called within the TCF dispatch thread.
+ *
+ * @param peerNode The peer model instance. Must not be <code>null</code>.
+ * @return The runtime model.
+ */
+ protected static IRuntimeModel initializeRuntimeModel(IPeerNode peerNode) {
+ Assert.isTrue(Protocol.isDispatchThread());
+ IRuntimeModel runtimeModel = new RuntimeModel(peerNode);
+ return runtimeModel;
+ }
+
+ /**
+ * Dispose the runtime model.
+ *
+ * @param peerNode The peer model instance. Must not be <code>null</code>.
+ */
+ public static void disposeRuntimeModel(final IPeerNode peerNode) {
+ Assert.isNotNull(peerNode);
+
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ // Get the peer id
+ String id = peerNode.getPeerId();
+ // Lookup the runtime model instance
+ IRuntimeModel candidate = runtimeModels.remove(id);
+ // Dispose it
+ if (candidate != null) candidate.dispose();
+ }
+ };
+
+ if (Protocol.isDispatchThread()) runnable.run();
+ else Protocol.invokeAndWait(runnable);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/RuntimeModel.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/RuntimeModel.java
new file mode 100644
index 000000000..359d8c413
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/RuntimeModel.java
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.model;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.te.core.interfaces.IConnectable;
+import org.eclipse.tcf.te.runtime.model.ContainerModelNode;
+import org.eclipse.tcf.te.runtime.model.factory.Factory;
+import org.eclipse.tcf.te.runtime.model.interfaces.factory.IFactory;
+import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelService;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime.IRuntimeModel;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+
+/**
+ * The file system model implementation.
+ */
+public final class RuntimeModel extends ContainerModelNode implements IRuntimeModel {
+ // Flag to mark the model disposed
+ private boolean disposed;
+
+ // Reference to the model node factory
+ private IFactory factory = null;
+
+ // The root node of the peer model
+ private FSTreeNode root;
+ private IPeerNode peerNode;
+
+ /**
+ * Create a File System ModelManager.
+ */
+ public RuntimeModel(IPeerNode peerNode) {
+ disposed = false;
+ this.peerNode = peerNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.nodes.PropertiesContainer#checkThreadAccess()
+ */
+ @Override
+ protected boolean checkThreadAccess() {
+ return Protocol.isDispatchThread();
+ }
+
+ /* (non-Javadoc)
+ * @see com.windriver.te.tcf.core.model.interfaces.IModel#dispose()
+ */
+ @Override
+ public void dispose() {
+ Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
+ disposed = true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.windriver.te.tcf.core.model.interfaces.IModel#isDisposed()
+ */
+ @Override
+ public boolean isDisposed() {
+ Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
+ return disposed;
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.windriver.te.tcf.core.model.interfaces.IModel#getService(java.lang.Class)
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends IModelService> V getService(Class<V> serviceInterface) {
+ Assert.isNotNull(serviceInterface);
+ return (V)getAdapter(serviceInterface);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (IPeerNode.class.isAssignableFrom(adapter) || IConnectable.class.isAssignableFrom(adapter)) {
+ final AtomicReference<IPeerNode> peerNode = new AtomicReference<IPeerNode>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ peerNode.set(getPeerNode());
+ }
+ });
+ return peerNode.get();
+ }
+
+ return super.getAdapter(adapter);
+ }
+
+ /* (non-Javadoc)
+ * @see com.windriver.te.tcf.core.model.interfaces.IModel#setFactory(com.windriver.te.tcf.core.model.interfaces.IModelNodeFactory)
+ */
+ @Override
+ public void setFactory(IFactory factory) {
+ Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
+ this.factory = factory;
+ }
+
+ /* (non-Javadoc)
+ * @see com.windriver.te.tcf.core.model.interfaces.IModel#getFactory()
+ */
+ @Override
+ public IFactory getFactory() {
+ Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
+ return factory != null ? factory : Factory.getInstance();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProvider#getPeerModel()
+ */
+ @Override
+ public IPeerNode getPeerNode() {
+ Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
+ return peerNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.model.ModelNode#toString()
+ */
+ @Override
+ public String toString() {
+ if (disposed) {
+ return "*DISPOSED* : " + super.toString(); //$NON-NLS-1$
+ }
+ return super.toString();
+ }
+
+ /**
+ * Get the root node of the peer model.
+ *
+ * @return The root node.
+ */
+ @Override
+ public FSTreeNode getRoot() {
+ if(root == null) {
+ root = createRoot();
+ }
+ return root;
+ }
+
+ /**
+ * Create a root node for the specified peer.
+ *
+ * @param peerNode The peer.
+ */
+ /* default */ FSTreeNode createRoot() {
+ if (Protocol.isDispatchThread()) {
+ return createRootNode(peerNode);
+ }
+ else {
+ final AtomicReference<FSTreeNode> ref = new AtomicReference<FSTreeNode>();
+ Protocol.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ ref.set(createRoot());
+ }
+ });
+ return ref.get();
+ }
+ }
+
+ /**
+ * Create a root node for the peer.
+ *
+ * @param peerNode The peer.
+ * @return The root file system node.
+ */
+ public static FSTreeNode createRootNode(IPeerNode peerNode) {
+ FSTreeNode node = new FSTreeNode();
+ node.type = "FSRootNode"; //$NON-NLS-1$
+ node.peerNode = peerNode;
+ node.name = Messages.FSTreeNodeContentProvider_rootNode_label;
+ return node;
+ }
+
+ /**
+ * Create a file node under the folder specified folder using the new name.
+ *
+ * @param name The file's name.
+ * @param folder The parent folder.
+ * @return The file tree node.
+ */
+ public static FSTreeNode createFileNode(String name, FSTreeNode folder) {
+ return createTreeNode(name, "FSFileNode", folder); //$NON-NLS-1$
+ }
+
+ /**
+ * Create a folder node under the folder specified folder using the new name.
+ *
+ * @param name The folder's name.
+ * @param folder The parent folder.
+ * @return The folder tree node.
+ */
+ public static FSTreeNode createFolderNode(String name, FSTreeNode folder) {
+ return createTreeNode(name, "FSDirNode", folder); //$NON-NLS-1$
+ }
+
+ /**
+ * Create a tree node under the folder specified folder using the new name.
+ *
+ * @param name The tree node's name.
+ * @param type The new node's type.
+ * @param folder The parent folder.
+ * @return The tree node.
+ */
+ private static FSTreeNode createTreeNode(String name, String type, FSTreeNode folder) {
+ FSTreeNode node = new FSTreeNode();
+ node.name = name;
+ node.parent = folder;
+ node.peerNode = folder.peerNode;
+ node.type = type;
+ return node;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/UserAccount.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/UserAccount.java
new file mode 100644
index 000000000..d501473e8
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/model/UserAccount.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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) - [352302]Opening a file in an editor depending on
+ * the client's permissions.
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.model;
+
+/**
+ * The data model of a user account.
+ */
+public class UserAccount {
+ // The user's id.
+ private int uid;
+ // The user's group id.
+ private int gid;
+ // The user's effective id.
+ private int euid;
+ // The user's effective group id.
+ private int egid;
+ // The user's home directory.
+ private String home;
+
+ /**
+ * Create a user account with given data.
+ *
+ * @param uid
+ * The user's id
+ * @param gid
+ * The user's group id
+ * @param euid
+ * The user's effective id.
+ * @param egid
+ * The user's effective group id.
+ * @param home
+ * The user's home directory.
+ */
+ public UserAccount(int uid, int gid, int euid, int egid, String home) {
+ this.uid = uid;
+ this.gid = gid;
+ this.euid = euid;
+ this.egid = egid;
+ this.home = home;
+ }
+
+ /**
+ * Get the user's id.
+ *
+ * @return The user's id.
+ */
+ public int getUID() {
+ return uid;
+ }
+
+ /**
+ * Get the user's group id.
+ *
+ * @return The user's group id.
+ */
+ public int getGID() {
+ return gid;
+ }
+
+ /**
+ * Get the user's effective id.
+ *
+ * @return The user's effective id.
+ */
+ public int getEUID() {
+ return euid;
+ }
+
+ /**
+ * Get the user's effective group id.
+ *
+ * @return The user's effective group id.
+ */
+ public int getEGID() {
+ return egid;
+ }
+
+ /**
+ * Get the user's home directory.
+ *
+ * @return The user's home directory.
+ */
+ public String getHome() {
+ return home;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.java
new file mode 100644
index 000000000..b49b665e7
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 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) - [345384] Provide property pages for remote file system nodes
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.nls;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * File System plug-in externalized strings management.
+ */
+public class Messages extends NLS {
+
+ // The plug-in resource bundle name
+ private static final String BUNDLE_NAME = "org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages"; //$NON-NLS-1$
+
+ /**
+ * Static constructor.
+ */
+ static {
+ // Load message values from bundle file
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ // **** Declare externalized string id's down here *****
+
+ public static String FSTreeNodeContentProvider_rootNode_label;
+ public static String FSTreeNode_TypeFile;
+ public static String FSTreeNode_TypeFileFolder;
+ public static String FSTreeNode_TypeLocalDisk;
+ public static String FSTreeNode_TypeSystemFile;
+ public static String FSTreeNode_TypeUnknownFile;
+
+ public static String Operation_DeletingFileFailed;
+ public static String Operation_NotResponding;
+ public static String Operation_OpeningChannelFailureMessage;
+ public static String Operation_NoFileSystemError;
+ public static String Operation_CannotOpenDir;
+ public static String Operation_CopyNOfFile;
+ public static String Operation_CopyOfFile;
+ public static String Operation_CannotCreateDirectory;
+ public static String Operation_TimeoutOpeningChannel;
+
+ public static String OpCopy_Copying;
+ public static String OpCopy_CannotCopyFile;
+ public static String OpCopy_CopyingFile;
+
+ public static String OpCreate_TaskName;
+
+ public static String TcfURLConnection_NoFileHandleReturned;
+ public static String TcfURLConnection_NoPeerFound;
+ public static String TcfURLConnection_NoSuchTcfAgent;
+
+ public static String OpDelete_Deleting;
+ public static String OpDelete_PrepareToDelete;
+ public static String OpDelete_RemovingFileFolder;
+
+ public static String OpDownload_Downloading;
+ public static String OpDownload_DownloadingMultipleFiles;
+ public static String OpDownload_DownloadingSingleFile;
+
+ public static String OpMove_Moving;
+ public static String OpMove_FileExistsError;
+ public static String OpMove_FolderExistsError;
+ public static String OpMove_CannotMove;
+ public static String OpMove_MovingFile;
+
+ public static String OpOutStreamOp_DownloadingProgress;
+
+ public static String OpRefresh_RefreshJobTitle;
+
+ public static String OpRename_CannotRename;
+ public static String OpRename_TitleRename;
+
+ public static String OpStreamOp_SetReadOnlyFailed;
+ public static String OpStreamOp_Bytes;
+ public static String OpStreamOp_KBs;
+ public static String OpStreamOp_MBs;
+
+ public static String OpUpload_UploadingProgress;
+ public static String OpUpload_UploadNFiles;
+ public static String OpUpload_UploadSingleFile;
+
+ public static String TcfInputStream_NoDataAvailable;
+ public static String TcfInputStream_StreamClosed;
+ public static String TcfOutputStream_StreamClosed;
+ public static String TcfURLStreamHandlerService_ErrorURLFormat;
+ public static String TcfURLStreamHandlerService_IllegalCharacter;
+ public static String TcfURLStreamHandlerService_OnlyDiskPartError;
+
+ public static String CacheManager_MkdirFailed;
+
+ public static String FileTransferService_error_mkdirFailed;
+ public static String BlockingFileSystemProxy_TimeoutOpeningFile;
+ public static String BlockingFileSystemProxy_TimeoutClosingFile;
+ public static String BlockingFileSystemProxy_TimeoutReadingFile;
+ public static String BlockingFileSystemProxy_TimeoutWritingFile;
+ public static String BlockingFileSystemProxy_TimeoutStat;
+ public static String BlockingFileSystemProxy_TimeoutLstat;
+ public static String BlockingFileSystemProxy_TimeoutFstat;
+ public static String BlockingFileSystemProxy_TimeoutSetStat;
+ public static String BlockingFileSystemProxy_TimeoutFSetStat;
+ public static String BlockingFileSystemProxy_TimeoutOpeningDir;
+ public static String BlockingFileSystemProxy_TimeoutReadingDir;
+ public static String BlockingFileSystemProxy_TimeoutMakingDir;
+ public static String BlockingFileSystemProxy_TimeoutRemovingDir;
+ public static String BlockingFileSystemProxy_TimeoutListingRoots;
+ public static String BlockingFileSystemProxy_TimeoutRemovingFile;
+ public static String BlockingFileSystemProxy_TimeoutGettingRealPath;
+ public static String BlockingFileSystemProxy_TimeoutRenamingFile;
+ public static String BlockingFileSystemProxy_TimeoutReadingLink;
+ public static String BlockingFileSystemProxy_TimeoutSymLink;
+ public static String BlockingFileSystemProxy_TimeoutCopying;
+ public static String BlockingFileSystemProxy_TimeoutGettingUser;
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.properties
new file mode 100644
index 000000000..ad4dd962c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/nls/Messages.properties
@@ -0,0 +1,100 @@
+###############################################################################
+# Copyright (c) 2012 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
+###############################################################################
+
+BlockingFileSystemProxy_TimeoutOpeningFile=Timeout while opening the file ''{0}''.
+BlockingFileSystemProxy_TimeoutClosingFile=Timeout while closing the handle ''{0}''.
+BlockingFileSystemProxy_TimeoutReadingFile=Timeout while reading the handle ''{0}''.
+BlockingFileSystemProxy_TimeoutWritingFile=Timeout while writing the handle ''{0}''.
+BlockingFileSystemProxy_TimeoutStat=Timeout while getting the stat of ''{0}''.
+BlockingFileSystemProxy_TimeoutLstat=Timeout while getting the lstat of ''{0}''.
+BlockingFileSystemProxy_TimeoutFstat=Timeout while getting the fstat of ''{0}''.
+BlockingFileSystemProxy_TimeoutSetStat=Timeout while setting the stat of ''{0}''.
+BlockingFileSystemProxy_TimeoutFSetStat=Timeout while setting the fstat of ''{0}''.
+BlockingFileSystemProxy_TimeoutOpeningDir=Timeout while opening the directory ''{0}''.
+BlockingFileSystemProxy_TimeoutReadingDir=Timeout while reading the directory ''{0}''.
+BlockingFileSystemProxy_TimeoutMakingDir=Timeout while making the directory ''{0}''.
+BlockingFileSystemProxy_TimeoutRemovingDir=Timeout while removing the directory ''{0}''.
+BlockingFileSystemProxy_TimeoutListingRoots=Timeout while listing root directories.
+BlockingFileSystemProxy_TimeoutRemovingFile=Timeout while removing the file ''{0}''.
+BlockingFileSystemProxy_TimeoutGettingRealPath=Timeout while getting real path for ''{0}''.
+BlockingFileSystemProxy_TimeoutRenamingFile=Timeout while renaming the file from ''{0}'' to ''{1}''.
+BlockingFileSystemProxy_TimeoutReadingLink=Timeout while reading the link for ''{0}''.
+BlockingFileSystemProxy_TimeoutSymLink=Timeout while creating a link ''{0}'' for ''{1}''.
+BlockingFileSystemProxy_TimeoutCopying=Timeout while copying files from ''{0}'' to ''{1}''.
+BlockingFileSystemProxy_TimeoutGettingUser=Timeout while getting the user data.
+FSTreeNodeContentProvider_rootNode_label=File System
+FSTreeNode_TypeFile=File
+FSTreeNode_TypeFileFolder=File Folder
+FSTreeNode_TypeLocalDisk=Local Disk
+FSTreeNode_TypeSystemFile=System file
+FSTreeNode_TypeUnknownFile=Unknown File
+
+Operation_DeletingFileFailed=Deleting {0} failed\!
+Operation_NotResponding=The TCF agent on the target {0} is probably down. It is not responding.
+Operation_OpeningChannelFailureMessage=We cannot open a TCF channel to the target: {0}. It is caused by {1}.
+Operation_NoFileSystemError=This TCF agent, {0}, does not provide a file system service\!
+Operation_CannotOpenDir=Cannot open directory {0} because {1}
+Operation_CopyNOfFile=Copy ({0}) of {1}
+Operation_CopyOfFile=Copy of {0}
+Operation_CannotCreateDirectory=Cannot create the directory {0} because: {1}
+Operation_TimeoutOpeningChannel=Timeout while opening a channel\!
+
+OpCopy_Copying=Copying {0} ...
+OpCopy_CannotCopyFile=Cannot copy file {0} because: {1}
+OpCopy_CopyingFile=Copying files
+
+OpCreate_TaskName=Creating {0}...
+
+TcfURLConnection_NoFileHandleReturned=No file handle returned\!
+TcfURLConnection_NoPeerFound=Could not find the specified target with the ID {0}
+TcfURLConnection_NoSuchTcfAgent=TCF agent is already disconnected\!
+
+OpDelete_Deleting=Deleting files
+OpDelete_PrepareToDelete=Prepare to delete files ...
+OpDelete_RemovingFileFolder=Removing {0} ...
+
+OpDownload_Downloading=Downloading file {0}:{1}/{2}
+OpDownload_DownloadingMultipleFiles=Downloading {0} files...
+OpDownload_DownloadingSingleFile=Downloading file {0}...
+
+OpMove_Moving=Moving {0} ...
+OpMove_FileExistsError=Cannot replace {0}: There is already a file with the same name as the folder you are moving.
+OpMove_FolderExistsError=Cannot replace {0}: There is already a folder with the same name as the file you are moving.
+OpMove_CannotMove=Cannot move {0} because {1}
+OpMove_MovingFile=Moving files
+
+OpOutStreamOp_DownloadingProgress=Downloading {0}/{1}.
+
+OpRefresh_RefreshJobTitle=Refresh {0}
+
+OpRename_CannotRename=Cannot rename {0} because {1}
+OpRename_TitleRename=Rename
+
+OpStreamOp_SetReadOnlyFailed=Setting the read-only attribute of {0} failed\!
+OpStreamOp_Bytes=\ bytes
+OpStreamOp_KBs=\ KBs
+OpStreamOp_MBs=\ MBs
+
+OpUpload_UploadingProgress=Uploading file {0}: {1}/{2}
+OpUpload_UploadNFiles=Uploading {0} files...
+OpUpload_UploadSingleFile=Uploading file {0}...
+
+TcfInputStream_NoDataAvailable=No data available
+TcfInputStream_StreamClosed=Stream is already closed\!
+
+TcfOutputStream_StreamClosed=Stream is already closed\!
+
+TcfURLStreamHandlerService_ErrorURLFormat=Error TCF URL format\!
+TcfURLStreamHandlerService_IllegalCharacter=A Windows path does not permit character {0}.
+TcfURLStreamHandlerService_OnlyDiskPartError=A Windows path should not contain only disk part.
+
+CacheManager_MkdirFailed=Making directory {0} failed
+
+FileTransferService_error_mkdirFailed=Failed to create directory structure to {0}. \ No newline at end of file
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/services/FileTransferService.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/services/FileTransferService.java
new file mode 100644
index 000000000..418991906
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/services/FileTransferService.java
@@ -0,0 +1,523 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2014 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
+ * Anna Dushistova(Montavista) - [386484]Allow file transfer from target to host into existing directories
+ * Anna Dushistova(Montavista) - [387819]File Transfer stopped working
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.tcf.filesystem.core.services;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IFileSystem;
+import org.eclipse.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.runtime.services.interfaces.filetransfer.IFileTransferItem;
+import org.eclipse.tcf.te.runtime.utils.ProgressHelper;
+import org.eclipse.tcf.te.runtime.utils.StatusHelper;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFChannelException;
+import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation;
+import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
+import org.eclipse.tcf.util.TCFFileInputStream;
+import org.eclipse.tcf.util.TCFFileOutputStream;
+
+/**
+ * TCF file transfer service.
+ */
+public class FileTransferService {
+
+ /**
+ * Returns the target path file attribute.
+ *
+ * @param peer The peer, must not be <code>null</code>.
+ * @param channel The channel or <code>null</code>.
+ * @param item The file transfer item, must not be <code>null</code>.
+ *
+ * @return The target path file attributes or <code>null</code>.
+ */
+ public static FileAttrs getRemoteFileAttrs(IPeer peer, IChannel channel, IFileTransferItem item) {
+
+ final AtomicReference<FileAttrs> attrs = new AtomicReference<FileAttrs>();
+
+ boolean ownChannel = false;
+ IFileSystem fileSystem;
+ try {
+ if (channel == null) {
+ ownChannel = true;
+ channel = Operation.openChannel(peer);
+ }
+ fileSystem = Operation.getBlockingFileSystem(channel);
+
+ Assert.isNotNull(fileSystem);
+
+ IPath targetPath = item.getTargetPath();
+ if (targetPath != null) {
+ final AtomicReference<FileSystemException> error = new AtomicReference<FileSystemException>();
+
+ fileSystem.stat(targetPath.toString(), new IFileSystem.DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException e, FileAttrs a) {
+ error.set(e);
+ attrs.set(e == null ? a : null);
+ }
+ });
+ }
+ if (ownChannel) {
+ closeChannel(peer, channel);
+ }
+ }
+ catch (Exception e) {
+ attrs.set(null);
+ }
+
+ return attrs.get();
+ }
+
+ /**
+ * Transfer a file between host and target depending on the {@link IFileTransferItem} data.
+ *
+ * @param peer The peer, must not be <code>null</code>.
+ * @param channel The channel or <code>null</code>.
+ * @param item The file transfer item, must not be <code>null</code>.
+ * @param monitor The progress monitor or <code>null</code>.
+ * @param callback The callback or <code>null</code>.
+ */
+ public static void transfer(IPeer peer, IChannel channel, IFileTransferItem item, IProgressMonitor monitor, ICallback callback) {
+
+ boolean ownChannel = false;
+ IFileSystem fileSystem;
+ try {
+ if (channel == null) {
+ ownChannel = true;
+ channel = Operation.openChannel(peer);
+ }
+ fileSystem = Operation.getBlockingFileSystem(channel);
+
+ Assert.isNotNull(fileSystem);
+
+ // Check the direction of the transfer
+ if (item.getDirection() == IFileTransferItem.TARGET_TO_HOST) {
+ transferToHost(peer, fileSystem, item, monitor, callback);
+ }
+ else {
+ transferToTarget(peer, fileSystem, item, monitor, callback);
+ }
+ if (ownChannel) {
+ closeChannel(peer, channel);
+ }
+ }
+ catch (Exception e) {
+ if (callback != null) {
+ callback.done(peer, StatusHelper.getStatus(e));
+ }
+ }
+ }
+
+ /**
+ * Transfer a file between host and target depending on the {@link IFileTransferItem} data.
+ *
+ * @param peer The peer, must not be <code>null</code>.
+ * @param item The file transfer item, must not be <code>null</code>.
+ * @param monitor The progress monitor or <code>null</code>.
+ * @param callback The callback or <code>null</code>.
+ */
+ protected static void transfer(IPeer peer, IFileTransferItem item, IProgressMonitor monitor, ICallback callback) {
+
+ // Check if we can skip the transfer
+ if (!item.isEnabled()) {
+ if (callback != null) {
+ callback.done(peer, Status.OK_STATUS);
+ }
+ return;
+ }
+
+ try {
+ IChannel channel = Operation.openChannel(peer);
+ transfer(peer, channel, item, monitor, callback);
+ closeChannel(peer, channel);
+ }
+ catch (Exception e) {
+ if (callback != null) {
+ callback.done(peer, StatusHelper.getStatus(e));
+ }
+ }
+ }
+
+ protected static void transferToHost(IPeer peer, IFileSystem fileSystem, IFileTransferItem item, IProgressMonitor monitor, ICallback callback) {
+
+ IStatus result = Status.OK_STATUS;
+
+ IPath hostPath = item.getHostPath();
+ IPath targetPath = item.getTargetPath();
+
+ BufferedOutputStream outStream = null;
+ TCFFileInputStream inStream = null;
+
+ final IFileSystem.IFileHandle[] handle = new IFileSystem.IFileHandle[1];
+ final FileSystemException[] error = new FileSystemException[1];
+ final IFileSystem.FileAttrs[] attrs = new IFileSystem.FileAttrs[1];
+
+ // Create necessary parent directory structure on host side
+ boolean rc = hostPath.removeLastSegments(1).toFile().exists();
+ if(!rc){
+ rc = hostPath.removeLastSegments(1).toFile().mkdirs();
+ if (!rc) {
+ IOException e = new IOException(NLS.bind(Messages.FileTransferService_error_mkdirFailed, hostPath.removeLastSegments(1).toOSString()));
+ result = StatusHelper.getStatus(e);
+ if (callback != null) callback.done(peer, result);
+ return;
+ }
+ }
+ // If the host file is a directory, append the remote file name
+ if (hostPath.toFile().isDirectory()) {
+ hostPath = item.getHostPath().append(targetPath.lastSegment());
+ }
+
+ // Remember the modification time of the remote file.
+ // We need this value to set the modification time of the host file
+ // _after_ the stream closed.
+ long mtime = -1;
+
+ try {
+ // Open the remote file
+ fileSystem.open(targetPath.toString(), IFileSystem.TCF_O_READ, null, new IFileSystem.DoneOpen() {
+ @Override
+ public void doneOpen(IToken token, FileSystemException e, IFileHandle h) {
+ error[0] = e;
+ handle[0] = h;
+ }
+ });
+ if (error[0] != null) {
+ throw error[0];
+ }
+ // Get the remote file attributes
+ fileSystem.fstat(handle[0], new IFileSystem.DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException e, FileAttrs a) {
+ error[0] = e;
+ attrs[0] = a;
+ }
+ });
+ if (error[0] != null) {
+ throw error[0];
+ }
+ // Remember the modification time
+ mtime = attrs[0].mtime;
+
+ // Open a output stream to the host file
+ outStream = new BufferedOutputStream(new FileOutputStream(hostPath.toFile()));
+ // And open the input stream to the target file handle
+ inStream = new TCFFileInputStream(handle[0]);
+
+ ProgressHelper.setSubTaskName(monitor, "Transfer '" + targetPath.toString() + "' to '" + hostPath.toOSString() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ long bytesTotal = attrs[0].size;
+ copy(inStream, outStream, bytesTotal, monitor);
+ }
+ catch (OperationCanceledException e) {
+ result = Status.CANCEL_STATUS;
+ }
+ catch (Exception e) {
+ result = StatusHelper.getStatus(e);
+ }
+ finally {
+ // Close all streams and cleanup
+ if (outStream != null) {
+ try {
+ outStream.close();
+ outStream = null;
+ }
+ catch (IOException e) {
+ }
+ }
+ if (inStream != null) {
+ try {
+ inStream.close();
+ inStream = null;
+ }
+ catch (IOException e) {
+ }
+ }
+
+ if (result.isOK()) {
+ if (mtime >= 0) {
+ rc = hostPath.toFile().setLastModified(mtime);
+ if (!rc && Platform.inDebugMode()) {
+ System.err.println("Failed to set mtime for " + hostPath.toOSString()); //$NON-NLS-1$
+ }
+ }
+ }
+ else if (result.getSeverity() == IStatus.ERROR || result.getSeverity() == IStatus.CANCEL) {
+ try {
+ rc = hostPath.toFile().delete();
+ if (!rc && Platform.inDebugMode()) {
+ System.err.println("Failed to delete host file " + hostPath.toOSString()); //$NON-NLS-1$
+ }
+ }
+ catch (Throwable e) {
+ }
+ }
+ }
+ if (callback != null) callback.done(peer, result);
+ }
+
+ protected static void transferToTarget(IPeer peer, IFileSystem fileSystem, IFileTransferItem item, IProgressMonitor monitor, ICallback callback) {
+
+ IStatus result = Status.OK_STATUS;
+
+ IPath targetPath = item.getTargetPath();
+ IPath hostPath = item.getHostPath();
+
+ BufferedInputStream inStream = null;
+ TCFFileOutputStream outStream = null;
+
+ final IFileSystem.IFileHandle[] handle = new IFileSystem.IFileHandle[1];
+ final FileSystemException[] error = new FileSystemException[1];
+ final FileAttrs[] attrs = new FileAttrs[1];
+
+ // Check the target destination directory
+ for (int i = 0; i < targetPath.segmentCount(); i++) {
+ IPath tp = i + 1 < targetPath.segmentCount() ? targetPath.removeLastSegments(targetPath.segmentCount() - (i + 1)) : targetPath;
+
+ error[0] = null;
+ attrs[0] = null;
+
+ fileSystem.stat(tp.toString(), new IFileSystem.DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException e, FileAttrs a) {
+ error[0] = e;
+ attrs[0] = a;
+ }
+ });
+
+ if (attrs[0] == null && i + 1 < targetPath.segmentCount()) {
+ error[0] = null;
+ attrs[0] = null;
+
+ fileSystem.mkdir(tp.toString(), null, new IFileSystem.DoneMkDir() {
+ @Override
+ public void doneMkDir(IToken token, FileSystemException e) {
+ error[0] = e;
+ }
+ });
+
+ if (error[0] != null) {
+ result = StatusHelper.getStatus(error[0]);
+ if (callback != null) callback.done(peer, result);
+ return;
+ }
+
+ // Read the attributes of the created directory
+ error[0] = null;
+ attrs[0] = null;
+
+ fileSystem.stat(tp.toString(), new IFileSystem.DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException e, FileAttrs a) {
+ error[0] = e;
+ attrs[0] = a;
+ }
+ });
+ }
+ }
+
+ // If we get the attributes back, the name at least exist in the target file system
+ if (attrs[0] != null && attrs[0].isDirectory()) {
+ targetPath = targetPath.append(item.getHostPath().lastSegment());
+ }
+
+ try {
+ // Open the remote file
+ fileSystem.open(targetPath.toString(), IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_TRUNC, null, new IFileSystem.DoneOpen() {
+ @Override
+ public void doneOpen(IToken token, FileSystemException e, IFileHandle h) {
+ error[0] = e;
+ handle[0] = h;
+ }
+ });
+ if (error[0] != null) {
+ throw error[0];
+ }
+
+ // Open a input stream from the host file
+ inStream = new BufferedInputStream(new FileInputStream(hostPath.toFile()));
+ // Open the output stream for the target file handle
+ outStream = new TCFFileOutputStream(handle[0]);
+
+ ProgressHelper.setSubTaskName(monitor, "Transfer '" + hostPath.toOSString() + "' to '" + targetPath.toString() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ copy(inStream, outStream, hostPath.toFile().length(), monitor);
+
+ // Get the remote file attributes
+ fileSystem.fstat(handle[0], new IFileSystem.DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException e, FileAttrs a) {
+ error[0] = e;
+ attrs[0] = a;
+ }
+ });
+ // Update the remote file attributes
+ IFileSystem.FileAttrs newAttrs = new FileAttrs(attrs[0].flags, attrs[0].size, attrs[0].uid, attrs[0].gid, attrs[0].permissions,
+ attrs[0].atime, hostPath.toFile().lastModified(), attrs[0].attributes);
+ // Set the remote file attributes
+ fileSystem.fsetstat(handle[0], newAttrs, new IFileSystem.DoneSetStat() {
+ @Override
+ public void doneSetStat(IToken token, FileSystemException e) {
+ error[0] = e;
+ }
+ });
+ }
+ catch (OperationCanceledException e) {
+ result = Status.CANCEL_STATUS;
+ }
+ catch (Exception e) {
+ result = StatusHelper.getStatus(e);
+ }
+ finally {
+ // Close all streams and cleanup
+ if (outStream != null) {
+ try {
+ outStream.close();
+ outStream = null;
+ }
+ catch (IOException e) {
+ }
+ }
+ if (inStream != null) {
+ try {
+ inStream.close();
+ inStream = null;
+ }
+ catch (IOException e) {
+ }
+ }
+
+ if (result.getSeverity() == IStatus.ERROR || result.getSeverity() == IStatus.CANCEL) {
+ fileSystem.remove(targetPath.toString(), new IFileSystem.DoneRemove() {
+ @Override
+ public void doneRemove(IToken token, FileSystemException error) {
+ }
+ });
+ }
+ }
+ if (callback != null) callback.done(peer, result);
+ }
+
+ private static void copy(InputStream in, OutputStream out, long bytesTotal, IProgressMonitor monitor) throws IOException {
+ long bytesDone = 0;
+ long speed;
+ long startTimeStamp = System.currentTimeMillis();
+ byte[] dataBuffer = new byte[12 * 1024];
+
+ // Copy from the input stream to the output stream (always binary).
+ while (true) {
+ if (ProgressHelper.isCanceled(monitor)) {
+ throw new OperationCanceledException();
+ }
+ // Read the data from the remote file
+ int bytesRead = in.read(dataBuffer);
+ // If reached EOF, we are done and break the loop
+ if (bytesRead < 0) {
+ break;
+ }
+ if (ProgressHelper.isCanceled(monitor)) {
+ throw new OperationCanceledException();
+ }
+ // Write back to the host file
+ out.write(dataBuffer, 0, bytesRead);
+
+ bytesDone += bytesRead;
+ long timestamp = System.currentTimeMillis();
+ speed = ((bytesDone) * 1000) / Math.max(timestamp - startTimeStamp, 1);
+
+ ProgressHelper.worked(monitor, (int)((bytesRead/(bytesTotal > 0 ? bytesTotal : 1000)) * 1000));
+ ProgressHelper.setSubTaskName(monitor, getProgressMessage(bytesDone, bytesTotal, speed));
+ }
+ }
+
+ /**
+ * Close the channel for file transfer.
+ * @param peer
+ * @param channel
+ * @throws TCFChannelException
+ */
+ protected static void closeChannel(final IPeer peer, final IChannel channel) throws TCFChannelException {
+ if (channel != null) {
+ Tcf.getChannelManager().closeChannel(channel);
+ }
+ }
+
+ private static String getProgressMessage(long bytesDone, long bytesTotal, long bytesSpeed) {
+ String done = "B"; //$NON-NLS-1$
+ String total = "B"; //$NON-NLS-1$
+ String speed = "B/s"; //$NON-NLS-1$
+
+ if (bytesDone > 1024) {
+ bytesDone /= 1024;
+ done = "KB"; //$NON-NLS-1$
+ }
+ if (bytesDone > 1024) {
+ bytesDone /= 1024;
+ done = "MB"; //$NON-NLS-1$
+ }
+ if (bytesDone > 1024) {
+ bytesDone /= 1024;
+ done = "GB"; //$NON-NLS-1$
+ }
+
+ if (bytesTotal > 1024) {
+ bytesTotal /= 1024;
+ total = "KB"; //$NON-NLS-1$
+ }
+ if (bytesTotal > 1024) {
+ bytesTotal /= 1024;
+ total = "MB"; //$NON-NLS-1$
+ }
+ if (bytesTotal > 1024) {
+ bytesTotal /= 1024;
+ total = "GB"; //$NON-NLS-1$
+ }
+
+ if (bytesSpeed > 1024) {
+ bytesSpeed /= 1024;
+ speed = "KB/s"; //$NON-NLS-1$
+ }
+ if (bytesSpeed > 1024) {
+ bytesSpeed /= 1024;
+ speed = "MB/s"; //$NON-NLS-1$
+ }
+ if (bytesSpeed > 1024) {
+ bytesSpeed /= 1024;
+ speed = "GB/s"; //$NON-NLS-1$
+ }
+
+ return bytesDone + done + " of " + (bytesTotal > 0 ? Long.toString(bytesTotal) : "N/A") + total + " at " + bytesSpeed + speed; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/DisposeModelStep.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/DisposeModelStep.java
new file mode 100644
index 000000000..2f03dde9a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/DisposeModelStep.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.steps;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.ModelManager;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+import org.eclipse.tcf.te.tcf.locator.steps.AbstractPeerNodeStep;
+
+/**
+ * Dispose the filesytsem runtime model associated with the peer node.
+ */
+public class DisposeModelStep extends AbstractPeerNodeStep {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStep#execute(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void execute(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor, ICallback callback) {
+ IPeerNode peerNode = getActivePeerModelContext(context, data, fullQualifiedId);
+ if (peerNode != null) ModelManager.disposeRuntimeModel(peerNode);
+ callback.done(this, Status.OK_STATUS);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStep#validateExecute(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void validateExecute(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) throws CoreException {
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/FileTransferStep.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/FileTransferStep.java
new file mode 100644
index 000000000..f38b862d6
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/FileTransferStep.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.steps;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer;
+import org.eclipse.tcf.te.runtime.services.interfaces.filetransfer.IFileTransferItem;
+import org.eclipse.tcf.te.runtime.stepper.StepperAttributeUtil;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext;
+import org.eclipse.tcf.te.tcf.core.interfaces.steps.ITcfStepAttributes;
+import org.eclipse.tcf.te.tcf.core.steps.AbstractPeerStep;
+import org.eclipse.tcf.te.tcf.filesystem.core.activator.CorePlugin;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.steps.IFileSystemStepAttributes;
+import org.eclipse.tcf.te.tcf.filesystem.core.services.FileTransferService;
+
+/**
+ * Launch process step implementation.
+ */
+public class FileTransferStep extends AbstractPeerStep {
+
+ /**
+ * Constructor.
+ */
+ public FileTransferStep() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IExtendedStep#validateExecute(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void validateExecute(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) throws CoreException {
+ IChannel channel = (IChannel)StepperAttributeUtil.getProperty(ITcfStepAttributes.ATTR_CHANNEL, fullQualifiedId, data);
+ if (channel == null || channel.getState() != IChannel.STATE_OPEN) {
+ throw new CoreException(new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), "missing or closed channel")); //$NON-NLS-1$
+ }
+
+ Object item = StepperAttributeUtil.getProperty(IFileSystemStepAttributes.ATTR_FILE_TRANSFER_ITEM, fullQualifiedId, data);
+ if (!(item instanceof IFileTransferItem)) {
+ throw new CoreException(new Status(IStatus.ERROR, CorePlugin.getUniqueIdentifier(), "missing file transfer item")); //$NON-NLS-1$
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStep#execute(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void execute(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor, final ICallback callback) {
+ final IChannel channel = (IChannel)StepperAttributeUtil.getProperty(ITcfStepAttributes.ATTR_CHANNEL, fullQualifiedId, data);
+ final IFileTransferItem item = (IFileTransferItem)StepperAttributeUtil.getProperty(IFileSystemStepAttributes.ATTR_FILE_TRANSFER_ITEM, fullQualifiedId, data);
+
+ if (item.isEnabled()) {
+ FileTransferService.transfer(getActivePeerContext(context, data, fullQualifiedId), channel, item, monitor, callback);
+ }
+ else {
+ callback.done(this, Status.OK_STATUS);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.extensions.AbstractStep#getTotalWork(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer)
+ */
+ @Override
+ public int getTotalWork(IStepContext context, IPropertiesContainer data) {
+ return 1000;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/InitializeModelStep.java b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/InitializeModelStep.java
new file mode 100644
index 000000000..fa5ea991d
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/steps/InitializeModelStep.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.filesystem.core.steps;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext;
+import org.eclipse.tcf.te.runtime.utils.StatusHelper;
+import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelChannelService;
+import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime.IRuntimeModel;
+import org.eclipse.tcf.te.tcf.filesystem.core.model.ModelManager;
+import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
+import org.eclipse.tcf.te.tcf.locator.steps.AbstractPeerNodeStep;
+
+/**
+ * Initialize the filesystem runtime model associated with the peer node.
+ */
+public class InitializeModelStep extends AbstractPeerNodeStep {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStep#execute(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void execute(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor, final ICallback callback) {
+ IPeerNode peerNode = getActivePeerModelContext(context, data, fullQualifiedId);
+ if (peerNode != null) {
+ IRuntimeModel model = ModelManager.getRuntimeModel(peerNode);
+ final IModelChannelService service = model != null ? model.getService(IModelChannelService.class) : null;
+ if (service != null) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ service.openChannel(new IModelChannelService.DoneOpenChannel() {
+ @Override
+ public void doneOpenChannel(Throwable error, IChannel channel) {
+ callback.done(InitializeModelStep.this, StatusHelper.getStatus(error));
+ }
+ });
+ }
+ };
+
+ Protocol.invokeLater(runnable);
+ } else {
+ callback.done(InitializeModelStep.this, Status.OK_STATUS);
+ }
+ } else {
+ callback.done(InitializeModelStep.this, Status.OK_STATUS);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStep#validateExecute(org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext, org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void validateExecute(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) throws CoreException {
+ }
+
+}

Back to the top