Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Stieber2011-10-04 08:40:58 +0000
committerUwe Stieber2011-10-04 08:40:58 +0000
commitaa381a6baa9a13213b3b3dcac74c0a2f021c5d63 (patch)
tree10f7724df4a2d8ca836c961a111ce3b757ad2d0d
parent5e4d0fd9e13179121f84622c5d395de00e92450f (diff)
downloadorg.eclipse.tcf-aa381a6baa9a13213b3b3dcac74c0a2f021c5d63.tar.gz
org.eclipse.tcf-aa381a6baa9a13213b3b3dcac74c0a2f021c5d63.tar.xz
org.eclipse.tcf-aa381a6baa9a13213b3b3dcac74c0a2f021c5d63.zip
Target Explorer: Bug 345552 - [TCF][File system] Edit the remote files
with a proper editor
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF3
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gifbin0 -> 338 bytes
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties23
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml569
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java39
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java44
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java61
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java91
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java5
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java50
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java62
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java193
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java142
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java256
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java479
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java306
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java408
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java158
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java100
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java104
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java26
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java40
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java40
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java41
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java430
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java37
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java90
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java33
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java12
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java46
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java76
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java45
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java38
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java259
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java89
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java167
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java55
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties55
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java3
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java61
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java67
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java170
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java146
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java205
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java22
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java102
-rw-r--r--target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java200
47 files changed, 5171 insertions, 477 deletions
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF
index 072f96b99..70e436d92 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/META-INF/MANIFEST.MF
@@ -18,6 +18,9 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.7.0",
org.eclipse.tm.te.tcf.locator;bundle-version="1.0.0",
org.eclipse.core.filesystem;bundle-version="1.3.100",
org.eclipse.ui.ide;bundle-version="3.7.0",
+ org.eclipse.compare;bundle-version="3.5.200",
+ org.eclipse.text;bundle-version="3.5.100",
+ org.eclipse.ui.workbench.texteditor;bundle-version="3.7.0",
org.eclipse.core.expressions;bundle-version="3.4.300"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gif b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gif
new file mode 100644
index 000000000..7437a027a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/icons/obj16/synch_synch.gif
Binary files differ
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties
index 7ddba5cac..aa6681902 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.properties
@@ -28,3 +28,26 @@ FSTreeViewerFilter.systemFiles=Protected operating system files (File System (TC
PropertiesAction.label=P&roperties
PropertiesAction.tooltip=Show Properties of Selection
+
+navigatorContent.name = Target File System (TCF)
+fsmenu.open.label = Open
+fsmenu.refresh.label = Refresh
+fsmenu.update.label = Update
+fsmenu.commit.label = Commit
+fsmenu.merge.label = Merge
+fsmenu.revert.label = Revert
+temenu.open.label = Open
+temenu.refresh.label = Refresh
+temenu.update.label = Update
+temenu.commit.label = Commit
+temenu.merge.label = Merge
+temenu.revert.label = Revert
+command.refresh.name = Refresh File
+command.update.name = Update File
+command.commit.name = Commit File
+command.merge.name = Merge File
+command.revert.name = Revert File
+decorator.modified.label = Modified Cache
+decorator.outdated.label = Outdated Cache
+decorator.conflict.label = Conflicting Cache
+preference.page.name = Target Explorer \ No newline at end of file
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml
index 6efc5e071..c5136680a 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/plugin.xml
@@ -25,11 +25,16 @@
icon="icons/obj16/rootdrive.gif"
id="org.eclipse.tm.te.tcf.filesystem.navigator.content"
labelProvider="org.eclipse.tm.te.tcf.filesystem.controls.FSTreeLabelProvider"
- name="Target File System (TCF)"
+ name="%navigatorContent.name"
priority="normal">
<triggerPoints>
- <instanceof value="org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel"/>
- <test property="org.eclipse.tm.te.tcf.locator.hasRemoteService" value="FileSystem"/>
+ <adapt
+ type="org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel">
+ <test
+ property="org.eclipse.tm.te.tcf.locator.hasRemoteService"
+ value="FileSystem">
+ </test>
+ </adapt>
</triggerPoints>
<possibleChildren>
<instanceof value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"/>
@@ -91,13 +96,13 @@
<menuContribution locationURI="popup:org.eclipse.tm.te.ui.controls.menu.fs?after=additions">
<separator name="group.open" visible="true"/>
<command
- commandId="org.eclipse.tm.te.tcf.filesystem.commands.open"
+ commandId="org.eclipse.ui.navigator.Open"
helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Open"
id="open"
- label="Open"
+ label="%fsmenu.open.label"
style="push">
<visibleWhen checkEnabled="false">
- <with variable="activeMenuSelection">
+ <with variable="selection">
<count value="1"/>
<iterate>
<instanceof value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"/>
@@ -112,6 +117,166 @@
</with>
</visibleWhen>
</command>
+ <separator
+ name="group.state"
+ visible="true">
+ </separator>
+ <command
+ commandId="org.eclipse.ui.file.refresh"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Refresh"
+ icon="platform:/plugin/org.eclipse.ui/icons/full/elcl16/refresh_nav.gif"
+ id="refresh"
+ label="%fsmenu.refresh.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.update"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Update"
+ id="update"
+ label="%fsmenu.update.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.commit"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Commit"
+ id="commit"
+ label="%fsmenu.commit.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.merge"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Merge"
+ id="merge"
+ label="%fsmenu.merge.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.revert"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Revert"
+ id="revert"
+ label="%fsmenu.revert.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
<separator name="group.properties" visible="true"/>
<command
commandId="org.eclipse.ui.file.properties"
@@ -152,10 +317,10 @@
</menuContribution>
<menuContribution locationURI="popup:org.eclipse.tm.te.ui.views.TargetExplorer#Popup?after=group.open">
<command
- commandId="org.eclipse.tm.te.tcf.filesystem.commands.open"
+ commandId="org.eclipse.ui.navigator.Open"
helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Open"
id="open"
- label="Open"
+ label="%temenu.open.label"
style="push">
<visibleWhen checkEnabled="false">
<with variable="selection">
@@ -173,6 +338,166 @@
</with>
</visibleWhen>
</command>
+ <separator
+ name="group.state"
+ visible="true">
+ </separator>
+ <command
+ commandId="org.eclipse.ui.file.refresh"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Refresh"
+ icon="platform:/plugin/org.eclipse.ui/icons/full/elcl16/refresh_nav.gif"
+ id="refresh"
+ label="%temenu.refresh.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.update"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Update"
+ id="update"
+ label="%temenu.update.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.commit"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Commit"
+ id="commit"
+ label="%temenu.commit.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.merge"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Merge"
+ id="merge"
+ label="%temenu.merge.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.revert"
+ helpContextId="org.eclipse.tm.te.tcf.filesystem.command_Revert"
+ id="revert"
+ label="%temenu.revert.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
+ </iterate>
+ </with>
+ <not>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.cache.isAutoSavingOn">
+ </test>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
</menuContribution>
</extension>
@@ -194,30 +519,40 @@
class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.FSTreeNodePropertyTester"
id="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode"
namespace="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode"
- properties="isFile,isDirectory,isBinaryFile,isReadable,isWritable,isExecutable"
+ properties="isFile,isDirectory,isBinaryFile,isReadable,isWritable,isExecutable,getCacheState"
type="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
</propertyTester>
+ <propertyTester
+ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.CachePropertyTester"
+ id="org.eclipse.tm.te.tcf.filesystem.propertytester.cache"
+ namespace="org.eclipse.tm.te.tcf.filesystem.propertytester.cache"
+ properties="isAutoSavingOn"
+ type="java.lang.Object">
+ </propertyTester>
</extension>
<!-- Target Explorer command contributions -->
<extension point="org.eclipse.ui.commands">
<command
- id="org.eclipse.tm.te.tcf.filesystem.commands.open"
- name="Open">
+ id="org.eclipse.tm.te.tcf.filesystem.commands.update"
+ name="%command.update.name">
+ </command>
+ <command
+ id="org.eclipse.tm.te.tcf.filesystem.commands.commit"
+ name="%command.commit.name">
+ </command>
+ <command
+ id="org.eclipse.tm.te.tcf.filesystem.commands.merge"
+ name="%command.merge.name">
+ </command>
+ <command
+ id="org.eclipse.tm.te.tcf.filesystem.commands.revert"
+ name="%command.revert.name">
</command>
</extension>
<!-- Target Explorer command handler contributions -->
<extension point="org.eclipse.ui.handlers">
- <handler
- class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.OpenFileHandler"
- commandId="org.eclipse.tm.te.tcf.filesystem.commands.open">
- <enabledWhen>
- <with variable="activeMenuSelection">
- <count value="1"/>
- </with>
- </enabledWhen>
- </handler>
<!-- This handler contribution is for the double click behaviour in the
Target Explore tree view -->
@@ -226,25 +561,203 @@
commandId="org.eclipse.ui.navigator.Open">
<activeWhen>
<and>
- <with variable="activePartId">
- <equals value="org.eclipse.tm.te.ui.views.TargetExplorer"/>
- </with>
- <with variable="selection">
- <count value="1"/>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
<iterate>
- <instanceof value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"/>
- <test property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile"/>
+ <instanceof
+ value="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode">
+ </instanceof>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isFile">
+ </test>
<test
+ forcePluginActivation="true"
property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isReadable">
</test>
<not>
- <test property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isBinaryFile"/>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.isBinaryFile">
+ </test>
</not>
</iterate>
</with>
+ <with
+ variable="activePartId">
+ <or>
+ <equals
+ value="org.eclipse.tm.te.ui.views.TargetExplorer">
+ </equals>
+ <equals
+ value="org.eclipse.tm.te.ui.view.Editor">
+ </equals>
+ </or>
+ </with>
</and>
</activeWhen>
</handler>
+ <handler
+ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.RefreshHandler"
+ commandId="org.eclipse.ui.file.refresh">
+ <activeWhen>
+ <with
+ variable="activePartId">
+ <or>
+ <equals
+ value="org.eclipse.tm.te.ui.views.TargetExplorer">
+ </equals>
+ <equals
+ value="org.eclipse.tm.te.ui.view.Editor">
+ </equals>
+ </or>
+ </with>
+ </activeWhen>
+ </handler>
+ <handler
+ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.UpdateHandler"
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.update">
+ <enabledWhen>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState"
+ value="outdated">
+ </test>
+ </iterate>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.CommitHandler"
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.commit">
+ <enabledWhen>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState"
+ value="modified">
+ </test>
+ </iterate>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.MergeHandler"
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.merge">
+ <enabledWhen>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState"
+ value="conflict">
+ </test>
+ </iterate>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="org.eclipse.tm.te.tcf.filesystem.internal.handlers.RevertHandler"
+ commandId="org.eclipse.tm.te.tcf.filesystem.commands.revert">
+ <enabledWhen>
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <or>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState"
+ value="modified">
+ </test>
+ <test
+ property="org.eclipse.tm.te.tcf.filesystem.propertytester.treenode.getCacheState"
+ value="conflict">
+ </test>
+ </or>
+ </iterate>
+ </with>
+ </enabledWhen>
+ </handler>
+ </extension>
+
+ <extension point="org.eclipse.ui.decorators">
+ <decorator
+ icon="icons/ovr/ovr_modified.png"
+ id="org.eclipse.tm.te.tcf.filesystem.decorators.modified"
+ label="%decorator.modified.label"
+ lightweight="true"
+ location="TOP_RIGHT"
+ state="true">
+ <enablement>
+ <objectState
+ name="cache.state"
+ value="modified">
+ </objectState>
+ </enablement>
+ </decorator>
+ <decorator
+ icon="icons/ovr/ovr_outdated.png"
+ id="org.eclipse.tm.te.tcf.filesystem.decorators.outdated"
+ label="%decorator.outdated.label"
+ lightweight="true"
+ location="TOP_RIGHT"
+ state="true">
+ <enablement>
+ <objectState
+ name="cache.state"
+ value="outdated">
+ </objectState>
+ </enablement>
+ </decorator>
+ <decorator
+ icon="icons/ovr/ovr_conflict.png"
+ id="org.eclipse.tm.te.tcf.filesystem.decorators.conflict"
+ label="%decorator.conflict.label"
+ lightweight="true"
+ location="TOP_RIGHT"
+ state="true">
+ <enablement>
+ <objectState
+ name="cache.state"
+ value="conflict">
+ </objectState>
+ </enablement>
+ </decorator>
+ </extension>
+
+ <extension point="org.eclipse.ui.preferencePages">
+ <page
+ class="org.eclipse.tm.te.tcf.filesystem.internal.preferences.TargetExplorerPreferencePage"
+ id="org.eclipse.tm.te.tcf.filesystem.preferencePage"
+ name="%preference.page.name">
+ </page>
+ </extension>
+
+ <extension point="org.eclipse.core.runtime.adapters">
+ <factory
+ adaptableType="org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode"
+ class="org.eclipse.tm.te.tcf.filesystem.internal.adapters.NodeStateFilterFactory">
+ <adapter
+ type="org.eclipse.ui.IActionFilter">
+ </adapter>
+ </factory>
</extension>
</plugin>
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java
index 08b0c33bf..e8b81ce5e 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/activator/UIPlugin.java
@@ -7,6 +7,7 @@
* 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.tm.te.tcf.filesystem.activator;
@@ -15,11 +16,19 @@ import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.IExecutionListener;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.graphics.Image;
import org.eclipse.tm.te.tcf.filesystem.internal.ImageConsts;
+import org.eclipse.tm.te.tcf.filesystem.internal.autosave.SaveAllListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.autosave.SaveListener;
import org.eclipse.tm.te.tcf.filesystem.internal.url.TcfURLConnection;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
@@ -27,16 +36,19 @@ import org.osgi.service.url.AbstractURLStreamHandlerService;
import org.osgi.service.url.URLConstants;
import org.osgi.service.url.URLStreamHandlerService;
-
/**
* The activator class controls the plug-in life cycle
*/
public class UIPlugin extends AbstractUIPlugin {
- // The shared instance
+ // The shared instance of this plug-in.
private static UIPlugin plugin;
// The service registration for the "tcf" URL stream handler.
private ServiceRegistration<?> regURLStreamHandlerService;
-
+ // The listener which listens to command "SAVE" and synchronize the local file with the target.
+ private IExecutionListener saveListener;
+ // The listener which listens to command "SAVE ALL" and synchronize the local file with the target.
+ private IExecutionListener saveAllListener;
+
/**
* The constructor
*/
@@ -81,6 +93,16 @@ public class UIPlugin extends AbstractUIPlugin {
return new TcfURLConnection(u);
}
}, properties);
+ // Add the two execution listeners to command "SAVE" and "SAVE ALL".
+ ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
+ if (commandService != null) {
+ saveListener = new SaveListener();
+ Command saveCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE);
+ saveCmd.addExecutionListener(saveListener);
+ saveAllListener = new SaveAllListener();
+ Command saveAllCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE_ALL);
+ saveAllCmd.addExecutionListener(saveAllListener);
+ }
}
/* (non-Javadoc)
@@ -92,6 +114,15 @@ public class UIPlugin extends AbstractUIPlugin {
regURLStreamHandlerService.unregister();
regURLStreamHandlerService = null;
}
+ // Remove the two execution listeners.
+ ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
+ if (commandService != null) {
+ Command saveCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE);
+ saveCmd.removeExecutionListener(saveListener);
+ Command saveAllCmd = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE_ALL);
+ saveAllCmd.removeExecutionListener(saveAllListener);
+ }
+ FSModel.getInstance().dispose();
plugin = null;
super.stop(context);
}
@@ -108,6 +139,8 @@ public class UIPlugin extends AbstractUIPlugin {
registry.put(ImageConsts.IMAGE_ROOT_DRIVE, ImageDescriptor.createFromURL(url));
url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "rootdriveopen.gif"); //$NON-NLS-1$
registry.put(ImageConsts.IMAGE_ROOT_DRIVE_OPEN, ImageDescriptor.createFromURL(url));
+ url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_OBJ + "synch_synch.gif"); //$NON-NLS-1$
+ registry.put(ImageConsts.IMAGE_COMPARE_EDITOR, ImageDescriptor.createFromURL(url));
}
/**
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java
index d35287e12..2184b2ada 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeContentProvider.java
@@ -15,8 +15,9 @@ import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.IToken;
@@ -27,6 +28,7 @@ import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle;
import org.eclipse.tm.te.tcf.core.Tcf;
import org.eclipse.tm.te.tcf.core.interfaces.IChannelManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.events.INodeStateListener;
import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel;
@@ -38,7 +40,7 @@ import org.eclipse.ui.PlatformUI;
/**
* Target Explorer: File system tree content provider implementation.
*/
-public class FSTreeContentProvider implements ITreeContentProvider {
+public class FSTreeContentProvider implements ITreeContentProvider, INodeStateListener {
/**
* Static reference to the return value representing no elements.
*/
@@ -46,19 +48,22 @@ public class FSTreeContentProvider implements ITreeContentProvider {
/**
* The file system model instance associated with this file system
- * tree content provider instance. Each content provider has it's own
- * file system mode instance.
+ * tree content provider instance.
*/
- /* default*/ final FSModel model = new FSModel();
+ /* package */ final static FSModel model = FSModel.getInstance();
- /* default */ Viewer viewer = null;
+ /* package */ TreeViewer viewer = null;
+
+ public FSTreeContentProvider(){
+ model.addNodeStateListener(this);
+ }
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- this.viewer = viewer;
+ this.viewer = (TreeViewer) viewer;
}
/* (non-Javadoc)
@@ -66,7 +71,7 @@ public class FSTreeContentProvider implements ITreeContentProvider {
*/
@Override
public void dispose() {
- model.dispose();
+ model.removeNodeStateListener(this);
}
/**
@@ -205,6 +210,7 @@ public class FSTreeContentProvider implements ITreeContentProvider {
node.parent = rootNode;
node.peerNode = rootNode.peerNode;
rootNode.getChildren().add(node);
+ model.addNode(node);
}
}
@@ -391,6 +397,7 @@ public class FSTreeContentProvider implements ITreeContentProvider {
node.parent = parentNode;
node.peerNode = parentNode.peerNode;
parentNode.getChildren().add(node);
+ model.addNode(node);
}
}
}
@@ -417,7 +424,7 @@ public class FSTreeContentProvider implements ITreeContentProvider {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
- if (viewer instanceof StructuredViewer) ((StructuredViewer)viewer).refresh(parentNode);
+ viewer.refresh(parentNode);
}
});
}
@@ -546,4 +553,23 @@ public class FSTreeContentProvider implements ITreeContentProvider {
return hasChildren;
}
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.events.INodeStateListener#stateChanged(org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode)
+ */
+ @Override
+ public void stateChanged(final FSTreeNode node) {
+ // Make sure that this node is inside of this viewer.
+ Display display = PlatformUI.getWorkbench().getDisplay();
+ if (display.getThread() == Thread.currentThread()) {
+ viewer.refresh(node);
+ } else {
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ viewer.refresh(node);
+ }
+ });
+ }
+ }
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java
index 7385574da..3722356f9 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeControl.java
@@ -10,7 +10,16 @@
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.controls;
+import java.util.Collections;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
@@ -23,15 +32,21 @@ import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
import org.eclipse.tm.te.ui.interfaces.IUIConstants;
import org.eclipse.tm.te.ui.trees.AbstractTreeControl;
+import org.eclipse.ui.IDecoratorManager;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.navigator.ICommonActionConstants;
import org.eclipse.ui.part.MultiPageSelectionProvider;
/**
* Target Explorer: File system browser control.
*/
-public class FSTreeControl extends AbstractTreeControl implements ISelectionChangedListener{
+public class FSTreeControl extends AbstractTreeControl implements ISelectionChangedListener, IDoubleClickListener {
/**
* Constructor.
@@ -71,6 +86,7 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan
column.setWidth(200);
}
tree.setHeaderVisible(hasColumns());
+ viewer.addDoubleClickListener(this);
}
/**
@@ -95,7 +111,11 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan
*/
@Override
protected ILabelProvider doCreateTreeViewerLabelProvider(TreeViewer viewer) {
- return new FSTreeLabelProvider(viewer);
+ FSTreeLabelProvider labelProvider = new FSTreeLabelProvider(viewer);
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IDecoratorManager manager = workbench.getDecoratorManager();
+ ILabelDecorator decorator = manager.getLabelDecorator();
+ return new FSTreeDecoratingLabelProvider(labelProvider,decorator);
}
/* (non-Javadoc)
@@ -130,8 +150,7 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan
return IUIConstants.ID_CONTROL_MENUS_BASE + ".menu.fs"; //$NON-NLS-1$;
}
- /*
- * (non-Javadoc)
+ /* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
@Override
@@ -149,4 +168,38 @@ public class FSTreeControl extends AbstractTreeControl implements ISelectionChan
}
}
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
+ */
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ // If an handled and enabled command is registered for the ICommonActionConstants.OPEN
+ // retargetable action id, redirect the double click handling to the command handler.
+ //
+ // Note: The default tree node expansion must be re-implemented in the active handler!
+ ICommandService service = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class);
+ Command command = service != null ? service.getCommand(ICommonActionConstants.OPEN) : null;
+ if (command != null && command.isDefined() && command.isEnabled()) {
+ try {
+ ISelection selection = event.getSelection();
+ EvaluationContext ctx = new EvaluationContext(null, selection);
+ ctx.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection);
+ ctx.addVariable(ISources.ACTIVE_MENU_SELECTION_NAME, selection);
+ ctx.addVariable(ISources.ACTIVE_WORKBENCH_WINDOW_NAME, PlatformUI.getWorkbench().getActiveWorkbenchWindow());
+ IWorkbenchPart part = getParentPart();
+ if (part != null) {
+ IWorkbenchPartSite site = part.getSite();
+ ctx.addVariable(ISources.ACTIVE_PART_ID_NAME, site.getId());
+ ctx.addVariable(ISources.ACTIVE_PART_NAME, part);
+ ctx.addVariable(ISources.ACTIVE_SITE_NAME, site);
+ ctx.addVariable(ISources.ACTIVE_SHELL_NAME, site.getShell());
+ }
+ ExecutionEvent executionEvent = new ExecutionEvent(command, Collections.EMPTY_MAP, part, ctx);
+ command.executeWithChecks(executionEvent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java
new file mode 100644
index 000000000..0b07e5f02
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/controls/FSTreeDecoratingLabelProvider.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.controls;
+
+import org.eclipse.jface.viewers.DecoratingLabelProvider;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelDecorator;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A subclass of DecoratingLabelProvider provides an FS Tree Viewer
+ * with a label provider which combines a nested label provider and an optional
+ * decorator. The decorator decorates the label text, image
+ * provided by the nested label provider.
+ *
+ */
+public class FSTreeDecoratingLabelProvider extends DecoratingLabelProvider implements ITableLabelProvider {
+
+ //The label provider for the execution context viewer.
+ private FSTreeLabelProvider fProvider;
+ //The label decorator decorating the above label provider.
+ private ILabelDecorator fDecorator;
+
+ /**
+ * Create a FSTreeDecoratingLabelProvider with an FSTreeLabelProvider and a decorator.
+ *
+ * @param provider The label provider to be decorated.
+ * @param decorator The label decorator.
+ */
+ public FSTreeDecoratingLabelProvider(FSTreeLabelProvider provider, ILabelDecorator decorator) {
+ super(provider, decorator);
+ fProvider = provider;
+ fDecorator = decorator;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
+ */
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ Image image = fProvider.getColumnImage(element, columnIndex);
+ if (columnIndex == 0 && fDecorator != null) {
+ if (fDecorator instanceof LabelDecorator) {
+ LabelDecorator ld2 = (LabelDecorator) fDecorator;
+ Image decorated = ld2.decorateImage(image, element, getDecorationContext());
+ if (decorated != null) {
+ return decorated;
+ }
+ } else {
+ Image decorated = fDecorator.decorateImage(image, element);
+ if (decorated != null) {
+ return decorated;
+ }
+ }
+ }
+ return image;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
+ */
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ String text = fProvider.getColumnText(element, columnIndex);
+ if (columnIndex == 0 && fDecorator != null) {
+ if (fDecorator instanceof LabelDecorator) {
+ LabelDecorator ld2 = (LabelDecorator) fDecorator;
+ String decorated = ld2.decorateText(text, element, getDecorationContext());
+ if (decorated != null) {
+ return decorated;
+ }
+ } else {
+ String decorated = fDecorator.decorateText(text, element);
+ if (decorated != null) {
+ return decorated;
+ }
+ }
+ }
+ return text;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java
index a5b6fb7e2..3a1a2b12d 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/ImageConsts.java
@@ -44,4 +44,9 @@ public interface ImageConsts {
* The key to access the base folder object image.
*/
public static final String IMAGE_ROOT_DRIVE_OPEN = "RootDriveOpen"; //$NON-NLS-1$
+
+ /**
+ * The key to access the image of compare editor.
+ */
+ public static final String IMAGE_COMPARE_EDITOR = "CompareEditor"; //$NON-NLS-1$
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java
new file mode 100644
index 000000000..f2fdb240b
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilter.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.adapters;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.StateManager;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IActionFilter;
+
+/**
+ * This action filter wraps an FSTreeNode and test its attribute of "cache.state".
+ * It serves as the expression filter of decorations of Target Explorer.
+ */
+public class NodeStateFilter implements IActionFilter {
+ private FSTreeNode node;
+
+ /**
+ * Constructor.
+ *
+ * @param node
+ * The wrapped tree node. Must not be <code>null</code>.
+ */
+ public NodeStateFilter(FSTreeNode node) {
+ Assert.isNotNull(node);
+ this.node = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ @Override
+ public boolean testAttribute(Object target, String name, String value) {
+ if (name.equals("cache.state") && node.isFile()) { //$NON-NLS-1$
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ if (value == null)
+ value = CacheState.consistent.name();
+ return value.equals(state.name());
+ }
+ return false;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java
new file mode 100644
index 000000000..0e3ca1d74
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/adapters/NodeStateFilterFactory.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.adapters;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IActionFilter;
+
+/**
+ * The adapter factory of <code>FSTreeNode</code> over <code>IActionFilter</code>
+ */
+@SuppressWarnings("rawtypes")
+public class NodeStateFilterFactory implements IAdapterFactory {
+ // The ADAPTERS adapted by this factory.
+ private static Class[] ADAPTERS = {IActionFilter.class};
+ // The fFilters map caching fFilters for FS nodes.
+ private Map<FSTreeNode, NodeStateFilter> filters;
+
+ /**
+ * Constructor.
+ */
+ public NodeStateFilterFactory(){
+ this.filters = Collections.synchronizedMap(new HashMap<FSTreeNode, NodeStateFilter>());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if(adaptableObject instanceof FSTreeNode){
+ FSTreeNode node = (FSTreeNode) adaptableObject;
+ NodeStateFilter filter = filters.get(node);
+ if(filter == null){
+ filter = new NodeStateFilter(node);
+ filters.put(node, filter);
+ }
+ return filter;
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
+ */
+ @Override
+ public Class[] getAdapterList() {
+ return ADAPTERS;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java
new file mode 100644
index 000000000..5c44dc833
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveAllListener.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.autosave;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.StateManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IURIEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The execution listener of command "SAVE ALL", which synchronizes the local
+ * file with the one on the target server after it is saved.
+ */
+public class SaveAllListener implements IExecutionListener {
+ // Dirty nodes that should be saved and synchronized.
+ private List<FSTreeNode> fDirtyNodes;
+ // The file system fModel storing the existing FSTreeNodes.
+ private FSModel fModel;
+ /**
+ * Create the listener listening to command "SAVE ALL".
+ */
+ public SaveAllListener() {
+ this.fModel = FSModel.getInstance();
+ this.fDirtyNodes = new ArrayList<FSTreeNode>();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteSuccess(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void postExecuteSuccess(String commandId, Object returnValue) {
+ if (!fDirtyNodes.isEmpty()) {
+ try {
+ List<FSTreeNode> modified = new ArrayList<FSTreeNode>();
+ List<FSTreeNode> conflicts = new ArrayList<FSTreeNode>();
+ for (FSTreeNode node : fDirtyNodes) {
+ // Refresh the dirty nodes and get their latest states.
+ StateManager.getInstance().refreshState(node);
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case consistent:
+ break;
+ case outdated:
+ break;
+ case modified:
+ // Reclassifying
+ modified.add(node);
+ break;
+ case conflict:
+ // Reclassifying
+ conflicts.add(node);
+ break;
+ }
+ }
+
+ if (CacheManager.getInstance().isAutoSaving()) {
+ // If auto saving is on.
+ if (!modified.isEmpty()) {
+ // Upload the modified nodes.
+ CacheManager.getInstance().upload(modified.toArray(new FSTreeNode[modified.size()]));
+ }
+ if (!conflicts.isEmpty()) {
+ // Merge the conflicting ones.
+ mergeConflicts(conflicts);
+ }
+ }
+ } catch (TCFException tcfe) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, tcfe.getLocalizedMessage());
+ }
+ }
+ }
+
+ /**
+ * Merge those conflicting nodes.
+ *
+ * @param conflicts The conflicting nodes.
+ */
+ private void mergeConflicts(List<FSTreeNode> conflicts) {
+ for (FSTreeNode node : conflicts) {
+ String title = Messages.SaveAllListener_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.SaveAllListener_SingularMessage, node.name);
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ Shell parent = window.getShell();
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message,
+ MessageDialog.QUESTION, new String[] { Messages.SaveAllListener_Merge,
+ Messages.SaveAllListener_SaveAnyway, Messages.SaveAllListener_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) { // Merge
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ MergeEditorInput mergeInput = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(mergeInput);
+ } else if (index == 1) { // Save anyway
+ CacheManager.getInstance().upload(conflicts.toArray(new FSTreeNode[conflicts.size()]));
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#preExecute(java.lang.String, org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public void preExecute(String commandId, ExecutionEvent event) {
+ fDirtyNodes.clear();
+ IWorkbenchPage page = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
+ IEditorPart[] editors = page.getDirtyEditors();
+ for (IEditorPart editor : editors) {
+ IEditorInput input = editor.getEditorInput();
+ FSTreeNode node = getEditedNode(input);
+ if (node != null) {
+ // If it is a modified node, add it to the dirty node list.
+ fDirtyNodes.add(node);
+ }
+ }
+ }
+
+ /**
+ * Get the corresponding FSTreeNode from the input.
+ * If the input has no corresponding FSTreeNode, return null;
+ * @param input The editor input.
+ * @return The corresponding FSTreeNode or null if it has not.
+ */
+ private FSTreeNode getEditedNode(IEditorInput input){
+ if (input instanceof IURIEditorInput) {
+ //Get the file that is being edited.
+ IURIEditorInput fileInput = (IURIEditorInput) input;
+ URI uri = fileInput.getURI();
+ try {
+ IFileStore store = EFS.getStore(uri);
+ File localFile = store.toLocalFile(0, new NullProgressMonitor());
+ if (localFile != null) {
+ // Get the file's mapped FSTreeNode.
+ FSTreeNode node = fModel.getTreeNode(localFile.toString());
+ return node;
+ }
+ }catch(CoreException e){}
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#notHandled(java.lang.String, org.eclipse.core.commands.NotHandledException)
+ */
+ @Override
+ public void notHandled(String commandId, NotHandledException exception) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteFailure(java.lang.String, org.eclipse.core.commands.ExecutionException)
+ */
+ @Override
+ public void postExecuteFailure(String commandId, ExecutionException exception) {
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java
new file mode 100644
index 000000000..a4ffef507
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/autosave/SaveListener.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.autosave;
+
+import java.io.File;
+import java.net.URI;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.StateManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IURIEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The execution listener of command "SAVE", which synchronizes the local file
+ * with the one on the target server after it is saved.
+ */
+public class SaveListener implements IExecutionListener {
+ // Dirty node that should be committed or merged.
+ private FSTreeNode dirtyNode;
+ // The file system fModel.
+ private FSModel model;
+
+ /**
+ * Create a SaveListener listening to command "SAVE".
+ */
+ public SaveListener() {
+ this.model = FSModel.getInstance();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteSuccess(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void postExecuteSuccess(String commandId, Object returnValue) {
+ if (dirtyNode != null) {
+ try{
+ // Refresh the fDirtyNode's state.
+ StateManager.getInstance().refreshState(dirtyNode);
+ if (CacheManager.getInstance().isAutoSaving()) {
+ CacheState state = StateManager.getInstance().getCacheState(dirtyNode);
+ switch (state) {
+ case conflict:
+ String title = Messages.SaveListener_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.SaveListener_StateChangedMessage, dirtyNode.name);
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ Shell parent = window.getShell();
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message, MessageDialog.QUESTION,
+ new String[] { Messages.SaveListener_Merge, Messages.SaveListener_SaveAnyway, Messages.SaveListener_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) {// Merge
+ LocalTypedElement local = new LocalTypedElement(dirtyNode);
+ RemoteTypedElement remote = new RemoteTypedElement(dirtyNode);
+ MergeEditorInput mergeInput = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(mergeInput);
+ } else if (index == 1) {// Save anyway.
+ CacheManager.getInstance().upload(dirtyNode);
+ }
+ break;
+ case modified:
+ // Save anyway
+ CacheManager.getInstance().upload(dirtyNode);
+ break;
+ case consistent:
+ break;
+ case outdated:
+ break;
+ }
+ }
+ }catch(TCFException tcfe){
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, tcfe.getLocalizedMessage());
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#preExecute(java.lang.String, org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public void preExecute(String commandId, ExecutionEvent event) {
+ dirtyNode = null;
+ IEditorInput input = HandlerUtil.getActiveEditorInput(event);
+ if (input instanceof IURIEditorInput) {
+ IURIEditorInput fileInput = (IURIEditorInput) input;
+ URI uri = fileInput.getURI();
+ try {
+ IFileStore store = EFS.getStore(uri);
+ File localFile = store.toLocalFile(0, new NullProgressMonitor());
+ if (localFile != null) {
+ dirtyNode = model.getTreeNode(localFile.toString());
+ }
+ }catch(CoreException e){
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#notHandled(java.lang.String, org.eclipse.core.commands.NotHandledException)
+ */
+ @Override
+ public void notHandled(String commandId, NotHandledException exception) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IExecutionListener#postExecuteFailure(java.lang.String, org.eclipse.core.commands.ExecutionException)
+ */
+ @Override
+ public void postExecuteFailure(String commandId, ExecutionException exception) {
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java
new file mode 100644
index 000000000..310302dfb
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/EditableSharedDocumentAdapter.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.IElementStateListener;
+
+/**
+ * A shared document adapter that tracks whether the element is connected to a
+ * shared document and whether the contents have been flushed from a compare
+ * viewer. When contents are flushed, this adapter will connect to the document
+ * provider to ensure that the changes are not lost (see
+ * {@link #hasBufferedContents()}). In order to avoid a leak, the buffer must
+ * either be saved (see
+ * {@link #saveDocument(IEditorInput, boolean, IProgressMonitor)}) or released
+ * (see {@link #releaseBuffer()}).
+ * <p>
+ * This adapter must have a one-to-one correspondence to a typed element.
+ *
+ * @since 3.7 - Copied from
+ * org.eclipse.team.internal.ui.synchronize.EditableSharedDocumentAdapter
+ */
+public class EditableSharedDocumentAdapter extends SharedDocumentAdapter implements IElementStateListener {
+
+ private int connectionCount;
+ private final ISharedDocumentAdapterListener listener;
+ private IEditorInput bufferedKey;
+
+ /**
+ * Interface that provides this adapter with the state of the typed element
+ * and supports call backs to the element when the adapter state changes.
+ */
+ public interface ISharedDocumentAdapterListener {
+
+ /**
+ * Method that is invoked when the adapter connects to the document
+ * provider. This method is only invoked when the adapter first connects
+ * to the document.
+ */
+ void handleDocumentConnected();
+
+ /**
+ * Method that is invoked when the adapter disconnects from the document
+ * provider. This method is only invoked when the adapter no longer has
+ * any connection to the document provider.
+ */
+ void handleDocumentDisconnected();
+
+ /**
+ * Method invoked when changes in the document are flushed to the
+ * adapter.
+ */
+ void handleDocumentFlushed();
+
+ /**
+ * Method invoked when the file behind the shared document is deleted.
+ */
+ void handleDocumentDeleted();
+
+ /**
+ * Method invoked when the document dirty state changes from dirty to
+ * clean.
+ */
+ void handleDocumentSaved();
+ }
+
+ /**
+ * Create the shared document adapter for the given element.
+ *
+ * @param listener
+ * access to element internals
+ */
+ public EditableSharedDocumentAdapter(ISharedDocumentAdapterListener listener) {
+ super();
+ this.listener = listener;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.SharedDocumentAdapter#connect(org.eclipse.ui.texteditor.IDocumentProvider, org.eclipse.ui.IEditorInput)
+ */
+ @Override
+ public void connect(IDocumentProvider provider, IEditorInput documentKey)
+ throws CoreException {
+ super.connect(provider, documentKey);
+ connectionCount++;
+ if (connectionCount == 1) {
+ provider.addElementStateListener(this);
+ listener.handleDocumentConnected();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.SharedDocumentAdapter#disconnect(org.eclipse.ui.texteditor.IDocumentProvider, org.eclipse.ui.IEditorInput)
+ */
+ @Override
+ public void disconnect(IDocumentProvider provider, IEditorInput documentKey) {
+ try {
+ super.disconnect(provider, documentKey);
+ } finally {
+ if (connectionCount > 0)
+ connectionCount--;
+ if (connectionCount == 0) {
+ provider.removeElementStateListener(this);
+ listener.handleDocumentDisconnected();
+ }
+ }
+ }
+
+ /**
+ * Return whether the element is connected to a shared document.
+ *
+ * @return whether the element is connected to a shared document
+ */
+ public boolean isConnected() {
+ return connectionCount > 0;
+ }
+
+ /**
+ * Save the shared document of the element of this adapter.
+ *
+ * @param input
+ * the document key of the element.
+ * @param monitor
+ * a progress monitor
+ * @return whether the save succeeded or not
+ * @throws CoreException
+ */
+ public boolean saveDocument(IEditorInput input, IProgressMonitor monitor)
+ throws CoreException {
+ if (isConnected()) {
+ IDocumentProvider provider = SharedDocumentAdapter
+ .getDocumentProvider(input);
+ try {
+ saveDocument(provider, input, provider.getDocument(input),
+ true, monitor);
+ } finally {
+ // When we write the document, remove out hold on the buffer
+ releaseBuffer();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Release the buffer if this adapter has buffered the contents in response
+ * to a
+ * {@link #flushDocument(IDocumentProvider, IEditorInput, IDocument, boolean)}
+ * .
+ */
+ public void releaseBuffer() {
+ if (bufferedKey != null) {
+ IDocumentProvider provider = SharedDocumentAdapter
+ .getDocumentProvider(bufferedKey);
+ provider.disconnect(bufferedKey);
+ bufferedKey = null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ISharedDocumentAdapter#flushDocument(org.eclipse.ui.texteditor.IDocumentProvider, org.eclipse.ui.IEditorInput, org.eclipse.jface.text.IDocument, boolean)
+ */
+ @Override
+ public void flushDocument(IDocumentProvider provider,
+ IEditorInput documentKey, IDocument document, boolean overwrite)
+ throws CoreException {
+ if (!hasBufferedContents()) {
+ // On a flush, make an extra connection to the shared document so it
+ // will be kept even
+ // if it is no longer being viewed.
+ bufferedKey = documentKey;
+ provider.connect(bufferedKey);
+ }
+ this.listener.handleDocumentFlushed();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementContentAboutToBeReplaced(java.lang.Object)
+ */
+ @Override
+ public void elementContentAboutToBeReplaced(Object element) {
+ // Nothing to do
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementContentReplaced(java.lang.Object)
+ */
+ @Override
+ public void elementContentReplaced(Object element) {
+ // Nothing to do
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementDeleted(java.lang.Object)
+ */
+ @Override
+ public void elementDeleted(Object element) {
+ listener.handleDocumentDeleted();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementDirtyStateChanged(java.lang.Object, boolean)
+ */
+ @Override
+ public void elementDirtyStateChanged(Object element, boolean isDirty) {
+ if (!isDirty) {
+ this.listener.handleDocumentSaved();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.texteditor.IElementStateListener#elementMoved(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public void elementMoved(Object originalElement, Object movedElement) {
+ // Nothing to do
+ }
+
+ /**
+ * Return whether the adapter has buffered contents. The adapter buffers
+ * contents by connecting to the document through the document provider.
+ * This means that the adapter must be disconnected either by saving or
+ * discarding the buffer.
+ *
+ * @return whether the adapter has buffered contents
+ */
+ public boolean hasBufferedContents() {
+ return bufferedKey != null;
+ }
+
+ /**
+ * Override getDocumentKey in the super class to provide an editor input
+ * when the element is a <code>LocalTypedElement</code>.
+ */
+ @Override
+ public IEditorInput getDocumentKey(Object element) {
+ if (element instanceof LocalTypedElement) {
+ LocalTypedElement localElement = (LocalTypedElement) element;
+ return localElement.getEditorInput();
+ }
+ return super.getDocumentKey(element);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java
new file mode 100644
index 000000000..be42037ed
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalFileSaveable.java
@@ -0,0 +1,479 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.IContentChangeListener;
+import org.eclipse.compare.IContentChangeNotifier;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.compare.contentmergeviewer.ContentMergeViewer;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.ISaveablesLifecycleListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartConstants;
+import org.eclipse.ui.Saveable;
+import org.eclipse.ui.SaveablesLifecycleEvent;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+
+/**
+ * A <code>LocalFileSaveable</code> is used as the saveable object that provides
+ * the save behavior for the merge editor input(<code>MergeEditorInput</code>).
+ */
+public class LocalFileSaveable extends Saveable implements
+ IPropertyChangeListener, ISharedDocumentAdapterListener,
+ IContentChangeListener {
+ // The property listener list.
+ private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+ // The merge input that provides the left and the right compared elements.
+ private final MergeInput input;
+
+ // The input of the editor that provides the comparing view.
+ private final MergeEditorInput editorInput;
+
+ // If the current document of the file is being saved.
+ private boolean saving;
+
+ // The local file element.
+ private LocalTypedElement fileElement;
+
+ // The document provided by the local file.
+ private IDocument document;
+
+ // If the current document has been connected.
+ private boolean connected;
+
+ /**
+ * Create the file-based saveable comparison.
+ *
+ * @param input
+ * the compare input to be save
+ * @param editorInput
+ * the editor input containing the comparison
+ * @param fileElement
+ * the file element that handles the saving and change
+ * notification
+ */
+ public LocalFileSaveable(MergeInput input, MergeEditorInput editorInput, LocalTypedElement fileElement) {
+ Assert.isNotNull(input);
+
+ this.input = input;
+ this.editorInput = editorInput;
+ this.fileElement = fileElement;
+ this.fileElement.addContentChangeListener(this);
+ this.fileElement.setDocumentListener(this);
+ }
+
+ /**
+ * Dispose of the saveable.
+ */
+ public void dispose() {
+ fileElement.removeContentChangeListener(this);
+ fileElement.discardBuffer();
+ fileElement.setDocumentListener(null);
+ }
+
+ /**
+ * Performs the save.
+ *
+ * @param monitor The progress monitor.
+ *
+ * @throws CoreException If the save operation fails.
+ */
+ protected void performSave(IProgressMonitor monitor) throws CoreException {
+ try {
+ saving = true;
+ monitor.beginTask(null, 100);
+ // First, we need to flush the viewers so the changes get buffered
+ // in the input
+ flushViewers(monitor);
+ // Then we tell the input to commit its changes
+ // Only the left is ever saveable
+ if (fileElement.isDirty()) {
+ if (fileElement.isConnected()) {
+ fileElement.store2Document(monitor);
+ } else {
+ fileElement.store2Cache(monitor);
+ }
+ }
+ } finally {
+ // Make sure we fire a change for the compare input to update the
+ // viewers
+ fireInputChange();
+ setDirty(false);
+ saving = false;
+ monitor.done();
+ //Trigger upload action
+ FSTreeNode node = fileElement.getFSTreeNode();
+ CacheManager.getInstance().upload(node);
+ }
+ }
+
+ /**
+ * Flush the contents of any viewers into the compare input.
+ *
+ * @param monitor
+ * a progress monitor
+ * @throws CoreException
+ */
+ protected void flushViewers(IProgressMonitor monitor) throws CoreException {
+ editorInput.saveChanges(monitor);
+ }
+
+ /**
+ * Fire an input change for the compare input after it has been saved.
+ */
+ protected void fireInputChange() {
+ editorInput.getCompareResult().fireInputChanged();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#isDirty()
+ */
+ @Override
+ public boolean isDirty() {
+ return editorInput.isSaveNeeded();
+ }
+
+ /**
+ * Marks the editor input dirty.
+ *
+ * @param dirty <code>True</code> if the editor is dirty, <code>false</code> if not.
+ */
+ protected void setDirty(boolean dirty) {
+ if (isDirty() != dirty) {
+ editorInput.setDirty(dirty);
+ firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getName()
+ */
+ @Override
+ public String getName() {
+ // Return the name of the file element as held in the compare input
+ if (fileElement.equals(input.getLeft())) {
+ return input.getLeft().getName();
+ }
+ if (fileElement.equals(input.getRight())) {
+ return input.getRight().getName();
+ }
+
+ // Fallback call returning name of the main non-null element of the input
+ //
+ // see org.eclipse.team.internal.ui.mapping.AbstractCompareInput#getMainElement()
+ return input.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getToolTipText()
+ */
+ @Override
+ public String getToolTipText() {
+ return editorInput.getToolTipText();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getImageDescriptor()
+ */
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ Image image = input.getImage();
+ if (image != null)
+ return ImageDescriptor.createFromImage(image);
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ @Override
+ public void propertyChange(PropertyChangeEvent e) {
+ String propertyName = e.getProperty();
+ if (CompareEditorInput.DIRTY_STATE.equals(propertyName)) {
+ boolean changed = false;
+ Object newValue = e.getNewValue();
+ if (newValue instanceof Boolean)
+ changed = ((Boolean) newValue).booleanValue();
+
+ ContentMergeViewer cmv = (ContentMergeViewer) e.getSource();
+
+ if (fileElement.equals(input.getLeft())) {
+ if (changed && cmv.internalIsLeftDirty())
+ setDirty(changed);
+ else if (!changed && !cmv.internalIsLeftDirty()) {
+ setDirty(changed);
+ }
+ }
+ if (fileElement.equals(input.getRight())) {
+ if (changed && cmv.internalIsRightDirty())
+ setDirty(changed);
+ else if (!changed && !cmv.internalIsRightDirty()) {
+ setDirty(changed);
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ if (document != null) {
+ return document.hashCode();
+ }
+ return input.hashCode();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (!(obj instanceof Saveable))
+ return false;
+
+ if (document != null) {
+ Object otherDocument = ((Saveable) obj).getAdapter(IDocument.class);
+
+ if (document == null && otherDocument == null)
+ return false;
+
+ return document != null && document.equals(otherDocument);
+ }
+
+ if (obj instanceof LocalFileSaveable) {
+ LocalFileSaveable saveable = (LocalFileSaveable) obj;
+ return saveable.input.equals(input);
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter == IDocument.class) {
+ if (document != null)
+ return document;
+ if (fileElement.isConnected()) {
+ ISharedDocumentAdapter sda = (ISharedDocumentAdapter) fileElement
+ .getAdapter(ISharedDocumentAdapter.class);
+ if (sda != null) {
+ IEditorInput input = sda.getDocumentKey(fileElement);
+ if (input != null) {
+ IDocumentProvider provider = SharedDocumentAdapter
+ .getDocumentProvider(input);
+ if (provider != null)
+ return provider.getDocument(input);
+ }
+ }
+ }
+ }
+ if (adapter == IEditorInput.class) {
+ return fileElement.getEditorInput();
+ }
+ return super.getAdapter(adapter);
+ }
+
+ /**
+ * Get an ISaveablesLifecycleListener from the specified workbench
+ * part.
+ * @param part The workbench part.
+ * @return The listener.
+ */
+ private ISaveablesLifecycleListener getSaveablesLifecycleListener(
+ IWorkbenchPart part) {
+ if (part instanceof ISaveablesLifecycleListener)
+ return (ISaveablesLifecycleListener) part;
+
+ Object adapted = part.getAdapter(ISaveablesLifecycleListener.class);
+ if (adapted instanceof ISaveablesLifecycleListener)
+ return (ISaveablesLifecycleListener) adapted;
+
+ adapted = Platform.getAdapterManager().loadAdapter(part,
+ ISaveablesLifecycleListener.class.getName());
+ if (adapted instanceof ISaveablesLifecycleListener)
+ return (ISaveablesLifecycleListener) adapted;
+
+ return (ISaveablesLifecycleListener) part.getSite().getService(
+ ISaveablesLifecycleListener.class);
+ }
+
+ /**
+ * When the document of the local file is connected, register this saveable.
+ */
+ private void registerSaveable() {
+ ICompareContainer container = editorInput.getContainer();
+ IWorkbenchPart part = container.getWorkbenchPart();
+ if (part != null) {
+ ISaveablesLifecycleListener lifecycleListener = getSaveablesLifecycleListener(part);
+ // Remove this saveable from the lifecycle listener
+ lifecycleListener.handleLifecycleEvent(new SaveablesLifecycleEvent(
+ part, SaveablesLifecycleEvent.POST_CLOSE,
+ new Saveable[] { this }, false));
+ // Now fix the hashing so it uses the fConnected fDocument
+ IDocument document = (IDocument) getAdapter(IDocument.class);
+ if (document != null) {
+ this.document = document;
+ }
+ // Finally, add this saveable back to the listener
+ lifecycleListener.handleLifecycleEvent(new SaveablesLifecycleEvent(
+ part, SaveablesLifecycleEvent.POST_OPEN,
+ new Saveable[] { this }, false));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentConnected()
+ */
+ @Override
+ public void handleDocumentConnected() {
+ if (connected)
+ return;
+ connected = true;
+ registerSaveable();
+ fileElement.setDocumentListener(null);
+ }
+
+ @Override
+ public void handleDocumentDeleted() {
+ // Ignore
+ }
+
+ @Override
+ public void handleDocumentDisconnected() {
+ // Ignore
+ }
+
+ @Override
+ public void handleDocumentFlushed() {
+ // Ignore
+ }
+
+ @Override
+ public void handleDocumentSaved() {
+ // Ignore
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.compare.IContentChangeListener#contentChanged(org.eclipse.compare.IContentChangeNotifier)
+ */
+ @Override
+ public void contentChanged(IContentChangeNotifier source) {
+ if (!saving) {
+ try {
+ performSave(new NullProgressMonitor());
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) throws CoreException {
+ if (isDirty()) {
+ performSave(monitor);
+ setDirty(false);
+ }
+ }
+
+ /**
+ * Revert any changes in the buffer back to the last saved state.
+ *
+ * @param monitor
+ * a progress monitor on <code>null</code> if progress feedback
+ * is not required
+ */
+ public void doRevert(IProgressMonitor monitor) {
+ if (!isDirty())
+ return;
+ fileElement.discardBuffer();
+ setDirty(false);
+ }
+
+ /**
+ * Add a property change listener. Adding a listener that is already
+ * registered has no effect.
+ *
+ * @param listener
+ * the listener
+ */
+ public void addPropertyListener(IPropertyListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Remove a property change listener. Removing a listener that is not
+ * registered has no effect.
+ *
+ * @param listener
+ * the listener
+ */
+ public void removePropertyListener(IPropertyListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Fire a property change event for this buffer.
+ *
+ * @param property
+ * the property that changed
+ */
+ protected void firePropertyChange(final int property) {
+ Object[] allListeners = listeners.getListeners();
+ for (int i = 0; i < allListeners.length; i++) {
+ final Object object = allListeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ ((IPropertyListener) object).propertyChanged(
+ LocalFileSaveable.this, property);
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // handled by platform
+ }
+ });
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java
new file mode 100644
index 000000000..fe6c66ec3
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/LocalTypedElement.java
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.compare;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.compare.IEditableContent;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.ide.FileStoreEditorInput;
+
+/**
+ * A <code>LocalTypedElement</code> extends <code>MergeTypedElement</code> and
+ * wraps an <code>FSTreeNode</code> so that it can be used as the left element
+ * of a <code>MergeEditorInput</code>. It implements the interface
+ * <code>IEditableContent</code> so that it is editable.
+ */
+public class LocalTypedElement extends MergeTypedElement implements
+ IEditableContent, IAdaptable, ISharedDocumentAdapterListener {
+ // If the current edited file is dirty.
+ private boolean dirty;
+ // The shared document adapter
+ private EditableSharedDocumentAdapter documentAdapter;
+ // The shared document listener.
+ private ISharedDocumentAdapterListener documentListener;
+
+ /**
+ * Creates a <code>LocalTypedElement</code> for the given resource.
+ *
+ * @param resource
+ * the resource
+ */
+ public LocalTypedElement(FSTreeNode node) {
+ super(node);
+ setContent(getContent());
+ dirty = false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter == ISharedDocumentAdapter.class) {
+ if (documentAdapter == null)
+ documentAdapter = new EditableSharedDocumentAdapter(this);
+ return documentAdapter;
+ }
+ return Platform.getAdapterManager().getAdapter(this, adapter);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.BufferedContent#setContent(byte[])
+ */
+ @Override
+ public void setContent(byte[] contents) {
+ dirty = true;
+ super.setContent(contents);
+ }
+
+ /**
+ * Set the document listener.
+ *
+ * @param documentListener
+ * the document listener.
+ */
+ public void setDocumentListener(
+ ISharedDocumentAdapterListener documentListener) {
+ this.documentListener = documentListener;
+ }
+
+ /**
+ * Return an input stream that opens that locally cached file to provide the
+ * content.
+ *
+ * @return a buffered input stream containing the contents of this file
+ * @exception CoreException
+ * if the contents of this storage could not be accessed
+ */
+ @Override
+ protected InputStream createStream() throws CoreException {
+ try {
+ IPath cachePath = CacheManager.getInstance().getCachePath(node);
+ File cacheFile = cachePath.toFile();
+ return new BufferedInputStream(new FileInputStream(cacheFile));
+ } catch (FileNotFoundException e) {
+ IStatus error = new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ throw new CoreException(error);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#isEditable()
+ */
+ @Override
+ public boolean isEditable() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.IEditableContent#replace(org.eclipse.compare.ITypedElement, org.eclipse.compare.ITypedElement)
+ */
+ @Override
+ public ITypedElement replace(ITypedElement dest, ITypedElement src) {
+ return dest;
+ }
+
+ /**
+ * Save the shared document for this element. The save can only be performed
+ * if the element is connected to a shared document. If the element is not
+ * connected, <code>false</code> is returned.
+ *
+ * @param overwrite
+ * indicates whether overwrite should be performed while saving
+ * the given element if necessary
+ * @param monitor
+ * a progress monitor
+ * @throws CoreException
+ */
+ public boolean store2Document(IProgressMonitor monitor)
+ throws CoreException {
+ if (isConnected()) {
+ IEditorInput input = documentAdapter.getDocumentKey(this);
+ documentAdapter.saveDocument(input, monitor);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Judges whether the content has been changed.
+ *
+ * @return
+ */
+ public boolean isDirty() {
+ return dirty
+ || (documentAdapter != null && documentAdapter
+ .hasBufferedContents());
+ }
+
+ /**
+ * Set the dirty state.
+ *
+ * @param dirty
+ * The dirty state.
+ */
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ }
+
+ /**
+ * If the document adapter has been connected.
+ *
+ * @return true if it is not null and connected.
+ */
+ public boolean isConnected() {
+ return documentAdapter != null && documentAdapter.isConnected();
+ }
+
+ /**
+ * Return the path to the local file of this node. It is used to compute its
+ * hash code and as the title of the comparison editor.
+ */
+ @Override
+ public String toString() {
+ File cacheFile = CacheManager.getInstance().getCacheFile(node);
+ return cacheFile.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentConnected()
+ */
+ @Override
+ public void handleDocumentConnected() {
+ if (documentListener != null)
+ documentListener.handleDocumentConnected();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentDeleted()
+ */
+ @Override
+ public void handleDocumentDeleted() {
+ if (documentListener != null)
+ documentListener.handleDocumentDeleted();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentDisconnected()
+ */
+ @Override
+ public void handleDocumentDisconnected() {
+ if (documentListener != null)
+ documentListener.handleDocumentDisconnected();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentFlushed()
+ */
+ @Override
+ public void handleDocumentFlushed() {
+ fireContentChanged();
+ if (documentListener != null)
+ documentListener.handleDocumentFlushed();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.te.tcf.filesystem.internal.compare.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener#handleDocumentSaved()
+ */
+ @Override
+ public void handleDocumentSaved() {
+ if (documentListener != null)
+ documentListener.handleDocumentSaved();
+ }
+
+ /**
+ * Get an editor input for this file.
+ *
+ * @return The editor input.
+ */
+ public IEditorInput getEditorInput() {
+ IPath path = CacheManager.getInstance().getCachePath(node);
+ IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
+ return new FileStoreEditorInput(fileStore);
+ }
+
+ /**
+ * Save to its local file when the document has not been connected yet.
+ *
+ * @param monitor
+ * The monitor that reports the progress.
+ */
+ public void store2Cache(IProgressMonitor monitor) throws CoreException {
+ File cacheFile = CacheManager.getInstance().getCacheFile(node);
+ monitor.beginTask(Messages.LocalTypedElement_SavingFile + cacheFile.getName(), 100);
+ InputStream is = getContents();
+ BufferedOutputStream bos = null;
+ try {
+ long total = cacheFile.length();
+ bos = new BufferedOutputStream(new FileOutputStream(cacheFile));
+ byte[] data = new byte[10 * 1024];
+ int length;
+ long current = 0;
+ int currProgress = 0;
+ while ((length = is.read(data)) > 0) {
+ bos.write(data, 0, length);
+ bos.flush();
+ current += length;
+ int progress = (int) (current * 100 / total);
+ if (currProgress != progress) {
+ monitor.worked(progress - currProgress);
+ currProgress = progress;
+ }
+ }
+ setDirty(false);
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e));
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException ex) {
+ }
+ if (bos != null) {
+ try {
+ bos.close();
+ } catch (Exception e) {
+ }
+ }
+ // Notify the local file element that the document has changed.
+ fireContentChanged();
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java
new file mode 100644
index 000000000..64341f04c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeEditorInput.java
@@ -0,0 +1,408 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.compare;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.IPropertyChangeNotifier;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.osgi.util.TextProcessor;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.ImageConsts;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.ISaveablesSource;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.Saveable;
+/**
+ * A <code>MergeEditorInput</code> is the input of a compare editor used to
+ * compare the local file and the remote file in a Target Explorer file system.
+ */
+public class MergeEditorInput extends CompareEditorInput implements
+ ISaveablesSource, IPropertyListener, ICompareInputChangeListener {
+
+ // The left element is a local file which can be editable.
+ private LocalTypedElement left;
+ // The right element is the remote file which is read only.
+ private RemoteTypedElement right;
+
+ // The active page of the current workbench.
+ private final IWorkbenchPage page;
+
+ // The current input change listener list.
+ private final ListenerList inputChangeListeners = new ListenerList(ListenerList.IDENTITY);
+ // The current saveable for the left element, i.e., the local file.
+ private LocalFileSaveable saveable;
+
+ /**
+ * Creates a new MergeEditorInput.
+ *
+ * @param left
+ * @param right
+ * @param page
+ */
+ public MergeEditorInput(LocalTypedElement left, RemoteTypedElement right, IWorkbenchPage page) {
+ super(new CompareConfiguration());
+ this.page = page;
+ this.left = left;
+ this.right = right;
+ configureCompare();
+ }
+
+ /**
+ * Configure the compare configuration using the left
+ * and right elements. Set the left and right label.
+ * Set the editable status.
+ */
+ protected void configureCompare() {
+ CompareConfiguration cc = getCompareConfiguration();
+ cc.setLeftEditable(true);
+ cc.setRightEditable(false);
+
+ String name = TextProcessor.process(left.getName());
+ String label = "Local: " + name; //$NON-NLS-1$
+ cc.setLeftLabel(label);
+
+ name = TextProcessor.process(right.toString());
+ label = "Remote: " + name; //$NON-NLS-1$
+ cc.setRightLabel(label);
+ }
+
+ /**
+ * Returns <code>true</code> if the other object is of type
+ * <code>MergeEditorInput</code> and both of their corresponding fLeft and
+ * fRight objects are identical. The content is not considered.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof MergeEditorInput) {
+ MergeEditorInput other = (MergeEditorInput) obj;
+ return other.left.equals(left) && other.right.equals(right);
+ }
+ return false;
+ }
+
+ /**
+ * Prepare the compare input of this editor input. This method is not
+ * intended to be overridden of extended by subclasses (but is not final for
+ * backwards compatibility reasons). The implementation of this method in
+ * this class delegates the creation of the compare input to the
+ * {@link #prepareCompareInput(IProgressMonitor)} method which subclasses
+ * must implement.
+ *
+ * @see org.eclipse.compare.CompareEditorInput#prepareInput(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ protected Object prepareInput(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ right.cacheContents(monitor);
+ MergeInput input = new MergeInput(left, right);
+ setTitle(input.getName());
+ return input;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getToolTipText()
+ */
+ @Override
+ public String getToolTipText() {
+ return "Compare " + left + " and " + right; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getTitle()
+ */
+ @Override
+ public String getTitle() {
+ return "Compare " + left.getName() + " with Local Cache"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Override the super method to provide clear typed input.
+ */
+ @Override
+ public MergeInput getCompareResult() {
+ return (MergeInput) super.getCompareResult();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#handleDispose()
+ */
+ @Override
+ protected void handleDispose() {
+ super.handleDispose();
+ ICompareInput compareInput = getCompareResult();
+ if (compareInput != null)
+ compareInput.removeCompareInputChangeListener(this);
+ if(getCompareResult()!=null){
+ getSaveable().removePropertyListener(this);
+ getSaveable().dispose();
+ }
+ left.discardBuffer();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#contentsCreated()
+ */
+ @Override
+ protected void contentsCreated() {
+ super.contentsCreated();
+ if (getCompareResult() != null) {
+ getCompareResult().addCompareInputChangeListener(this);
+ getSaveable().addPropertyListener(this);
+ setDirty(getSaveable().isDirty());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPropertyListener#propertyChanged(java.lang.Object, int)
+ */
+ @Override
+ public void propertyChanged(Object source, int propId) {
+ if (propId == IWorkbenchPartConstants.PROP_DIRTY && getCompareResult()!=null) {
+ setDirty(getSaveable().isDirty());
+ }
+ }
+
+ /**
+ * Close the editor if it is not dirty. If it is still dirty, let the
+ * content merge viewer handle the compare input change.
+ *
+ * @param checkForUnsavedChanges
+ * whether to check for unsaved changes
+ * @return <code>true</code> if the editor was closed (note that the close
+ * may be asynchronous)
+ */
+ protected boolean closeEditor(boolean checkForUnsavedChanges) {
+ if (isSaveNeeded() && checkForUnsavedChanges) {
+ return false;
+ }
+ final IWorkbenchPage page = getPage();
+ if (page == null)
+ return false;
+
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = page.getWorkbenchWindow().getShell();
+ if (shell == null)
+ return;
+
+ IEditorPart part = page.findEditor(MergeEditorInput.this);
+ getPage().closeEditor(part, false);
+ }
+ };
+ if (Display.getCurrent() != null) {
+ runnable.run();
+ } else {
+ Shell shell = page.getWorkbenchWindow().getShell();
+ if (shell == null)
+ return false;
+ Display display = shell.getDisplay();
+ display.asyncExec(runnable);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current active page.
+ * @return the workbench page.
+ */
+ IWorkbenchPage getPage() {
+ if (page == null)
+ return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ return page;
+ }
+
+ /**
+ * Propagate the input change event to the compare input change listener list.
+ */
+ /* default */void propogateInputChange() {
+ if (!inputChangeListeners.isEmpty()) {
+ Object[] allListeners = inputChangeListeners.getListeners();
+ for (int i = 0; i < allListeners.length; i++) {
+ final ICompareInputChangeListener listener = (ICompareInputChangeListener) allListeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ listener.compareInputChanged(getCompareResult());
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Logged by the safe runner
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Get the fSaveable that provides the save behavior for this compare editor
+ * input. The {@link #createSaveable()} is called to create the fSaveable if
+ * it does not yet exist. This method cannot be called until after the input
+ * is prepared (i.e. until after the {@link #run(IProgressMonitor)} method
+ * is called which will in turn will invoke
+ * {@link #prepareCompareInput(IProgressMonitor)}.
+ *
+ * @return fSaveable that provides the save behavior for this compare editor
+ * input.
+ */
+ public LocalFileSaveable getSaveable() {
+ if (saveable == null) {
+ assert getCompareResult() != null;
+ saveable = new LocalFileSaveable(getCompareResult(), this, left);
+ }
+ return saveable;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
+ */
+ @Override
+ public Saveable[] getActiveSaveables() {
+ if (getCompareResult() == null)
+ return new Saveable[0];
+ return new Saveable[] { getSaveable() };
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.ISaveablesSource#getSaveables()
+ */
+ @Override
+ public Saveable[] getSaveables() {
+ return getActiveSaveables();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void addCompareInputChangeListener(ICompareInput input,
+ ICompareInputChangeListener listener) {
+ if (input == getCompareResult()) {
+ inputChangeListeners.add(listener);
+ } else {
+ super.addCompareInputChangeListener(input, listener);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void removeCompareInputChangeListener(ICompareInput input,
+ ICompareInputChangeListener listener) {
+ if (input == getCompareResult()) {
+ inputChangeListeners.remove(listener);
+ } else {
+ super.removeCompareInputChangeListener(input, listener);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getTitleImage()
+ */
+ @Override
+ public Image getTitleImage() {
+ return UIPlugin.getImage(ImageConsts.IMAGE_COMPARE_EDITOR);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#getImageDescriptor()
+ */
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ return UIPlugin.getImageDescriptor(ImageConsts.IMAGE_COMPARE_EDITOR);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#findContentViewer(org.eclipse.jface.viewers.Viewer, org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public Viewer findContentViewer(Viewer oldViewer, ICompareInput input,
+ Composite parent) {
+ Viewer newViewer = super.findContentViewer(oldViewer, input, parent);
+ boolean isNewViewer = newViewer != oldViewer;
+ if (isNewViewer && newViewer instanceof IPropertyChangeNotifier && getCompareResult()!=null) {
+ // Register the model for change events if appropriate
+ final IPropertyChangeNotifier dsp = (IPropertyChangeNotifier) newViewer;
+ dsp.addPropertyChangeListener(getSaveable());
+ Control c = newViewer.getControl();
+ c.addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ dsp.removePropertyChangeListener(getSaveable());
+ }
+ });
+ }
+ return newViewer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#canRunAsJob()
+ */
+ @Override
+ public boolean canRunAsJob() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.CompareEditorInput#isDirty()
+ */
+ @Override
+ public boolean isDirty() {
+ if (saveable != null)
+ return saveable.isDirty();
+ return super.isDirty();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener#compareInputChanged(org.eclipse.compare.structuremergeviewer.ICompareInput)
+ */
+ @Override
+ public void compareInputChanged(ICompareInput source) {
+ if (source == getCompareResult()) {
+ boolean closed = false;
+ if (source.getKind() == Differencer.NO_CHANGE) {
+ closed = closeEditor(true);
+ }
+ if (!closed) {
+ // The editor was closed either because the compare
+ // input still has changes or because the editor input
+ // is dirty. In either case, fire the changes
+ // to the registered listeners
+ propogateInputChange();
+ }
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java
new file mode 100644
index 000000000..3d461e840
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeInput.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * An abstract compare input whose purpose is to support change notification
+ * through a {@link CompareInputChangeNotifier}.
+ */
+public class MergeInput implements ICompareInput {
+
+ // The left element.
+ private ITypedElement left;
+ // The right element.
+ private ITypedElement right;
+ // The compare input change listener list.
+ private final ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+ /**
+ * Create a <code>MergeInput</code>.
+ * @param left the left element.
+ * @param right the right element.
+ */
+ public MergeInput(ITypedElement left, ITypedElement right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void addCompareInputChangeListener(ICompareInputChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
+ */
+ @Override
+ public void removeCompareInputChangeListener(ICompareInputChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Fire a compare input change event. This method must be called from the UI
+ * thread.
+ */
+ void fireInputChanged() {
+ if (!listeners.isEmpty()) {
+ Object[] _listeners = listeners.getListeners();
+ for (int i = 0; i < _listeners.length; i++) {
+ final ICompareInputChangeListener listener = (ICompareInputChangeListener) _listeners[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ listener.compareInputChanged(MergeInput.this);
+ }
+
+ @Override
+ public void handleException(Throwable exception) {
+ // Logged by the safe runner
+ }
+ });
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#copy(boolean)
+ */
+ @Override
+ public void copy(boolean leftToRight) {
+ Assert.isTrue(false, "Copy is not support by this type of compare input"); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getAncestor()
+ */
+ @Override
+ public ITypedElement getAncestor() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getImage()
+ */
+ @Override
+ public Image getImage() {
+ ITypedElement element = getMainElement();
+ return element == null ? null : element.getImage();
+ }
+
+ /**
+ * Return the main non-null element that identifies this input. By default,
+ * the fLeft is returned if non-null. If the fLeft is null, the fRight is
+ * returned. If both the fLeft and fRight are null the ancestor is returned.
+ *
+ * @return the main non-null element that identifies this input
+ */
+ private ITypedElement getMainElement() {
+ if (left != null)
+ return left;
+ if (right != null)
+ return right;
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getKind()
+ */
+ @Override
+ public int getKind() {
+ return Differencer.CHANGE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getLeft()
+ */
+ @Override
+ public ITypedElement getLeft() {
+ return left;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getName()
+ */
+ @Override
+ public String getName() {
+ ITypedElement element = getMainElement();
+ return element == null ? null : element.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.ICompareInput#getRight()
+ */
+ @Override
+ public ITypedElement getRight() {
+ return right;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java
new file mode 100644
index 000000000..8eaf73b3c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/MergeTypedElement.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.compare;
+
+import org.eclipse.compare.BufferedContent;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
+/**
+ * A <code>MergeTypedElement</code> wraps an <code>FSTreeNode</code> so that it
+ * can be used as input for the differencing engine (<code>ITypedElement</code>).
+ */
+public abstract class MergeTypedElement extends BufferedContent implements ITypedElement {
+ // The File System tree node to be wrapped.
+ protected FSTreeNode node;
+
+ /**
+ * Create a MergeTypedElement for the given node.
+ *
+ * @param node
+ * The node.
+ */
+ public MergeTypedElement(FSTreeNode node) {
+ this.node = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getImage()
+ */
+ @Override
+ public Image getImage() {
+ return CompareUI.getImage(getType());
+ }
+
+ /**
+ * Return the tree node wrapped.
+ *
+ * @return The tree node of the file
+ */
+ public FSTreeNode getFSTreeNode() {
+ return node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getType()
+ */
+ @Override
+ public String getType() {
+ if (node.isDirectory())
+ return ITypedElement.FOLDER_TYPE;
+ if (node != null) {
+ String s = node.name;
+ int dot = s.lastIndexOf('.');
+ if (dot != -1)
+ s = s.substring(dot + 1);
+ if (s != null)
+ return s;
+ }
+ return ITypedElement.UNKNOWN_TYPE;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ITypedElement) {
+ return toString().equals(other.toString());
+ }
+ return super.equals(other);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.ITypedElement#getName()
+ */
+ @Override
+ public String getName() {
+ return node.name;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ *
+ * Returns the hash code of the name.
+ */
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java
new file mode 100644
index 000000000..75d543537
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/compare/RemoteTypedElement.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.compare;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+
+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.osgi.util.NLS;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
+/**
+ * A <code>RemoteTypedElement</code> wraps an <code>FSTreeNode</code> so that it
+ * can be used as input for the differencing engine (<code>ITypedElement</code>)
+ * as the right element of the comparison editor.
+ *
+ * @since 3.7
+ */
+public class RemoteTypedElement extends MergeTypedElement {
+ /**
+ * Creates a <code>RemoteTypedElement</code> for the given node.
+ *
+ * @param node
+ * the tree node.
+ */
+ public RemoteTypedElement(FSTreeNode node) {
+ super(node);
+ }
+
+ /**
+ * Return an input stream that opens that remote file to provide the stream
+ * content.
+ *
+ * @return a buffered input stream containing the contents of this file
+ * @exception CoreException
+ * if the contents of this storage could not be accessed
+ */
+ @Override
+ protected InputStream createStream() throws CoreException {
+ try {
+ return node.getLocationURL().openStream();
+ } catch (IOException e) {
+ Status error = new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ throw new CoreException(error);
+ }
+ }
+
+ /**
+ * Download the remote file and save the content so that it is cached for
+ * getContents call.
+ *
+ * @param monitor
+ * The monitor used to display downloading progress.
+ * @throws InvocationTargetException
+ * throws when an exception occurs during downloading.
+ */
+ public void cacheContents(IProgressMonitor monitor)
+ throws InvocationTargetException {
+ monitor.beginTask(
+ NLS.bind(Messages.CacheManager_DowloadingFile, node.name), 100);
+ OutputStream output = null;
+ try {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ output = new BufferedOutputStream(baos);
+ monitor.beginTask(Messages.RemoteTypedElement_GettingRemoteContent
+ + node.name, 100);
+ CacheManager.getInstance().download2OutputStream(node, output, monitor);
+ if (!monitor.isCanceled()) {
+ setContent(baos.toByteArray());
+ }
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ /**
+ * Return the external form of the URL to the remote file of this node. It
+ * is used to compute its hash code and as the title of the comparison
+ * editor.
+ */
+ @Override
+ public String toString() {
+ return node.getLocationURL().toString();
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java
new file mode 100644
index 000000000..9790e841d
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/events/INodeStateListener.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.events;
+
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+/**
+ * An INodeStateListener is a listener interface. Classes that implement this
+ * interface serve as a listener processing the event that a node state has changed.
+ *
+ */
+public interface INodeStateListener {
+ /**
+ * Fired when the state of the specified FSTreeNode has changed.
+ *
+ * @param node The FSTreeNode whose state has changed.
+ */
+ void stateChanged(FSTreeNode node);
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java
new file mode 100644
index 000000000..e8e22d6b5
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFChannelException.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.exceptions;
+
+/**
+ * TCF channel exception.
+ */
+public class TCFChannelException extends TCFException {
+ private static final long serialVersionUID = 7414816212710485160L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFChannelException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFChannelException(String message, Throwable cause){
+ super(message, cause);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java
new file mode 100644
index 000000000..40d5a3523
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFException.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.exceptions;
+
+/**
+ * TCF file system implementation base exception.
+ */
+public class TCFException extends Exception {
+ private static final long serialVersionUID = -220092425137980661L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java
new file mode 100644
index 000000000..7b5edac15
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/exceptions/TCFFileSystemException.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.exceptions;
+
+/**
+ * TCF remote file system exception.
+ */
+public class TCFFileSystemException extends TCFException {
+ private static final long serialVersionUID = -5203855887734608373L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ */
+ public TCFFileSystemException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * The exception detail message or <code>null</code>.
+ * @param cause
+ * The exception cause or <code>null</code>.
+ */
+ public TCFFileSystemException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java
index 2332963d1..9d2869cc9 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CacheManager.java
@@ -6,45 +6,68 @@
*
* 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)- [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.tm.te.tcf.filesystem.internal.handlers;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.DecimalFormat;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.internal.preferences.TargetExplorerPreferencePage;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.TcfURLConnection;
import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.PlatformUI;
/**
* The local file system cache used to manage the temporary files downloaded
* from a remote file system.
*/
public class CacheManager {
+
+ // Time stamp file used to persist the time stamps of each file.
+ private static final String TIMESTAMP_FILE = "timestamps.xml"; //$NON-NLS-1$
+
+ // The agent directory's prefixed name.
private static final String WS_AGENT_DIR_PREFIX = "agent_"; //$NON-NLS-1$
- // The singleton instance.
- private static CacheManager instance;
- // The formatter used to format the size displayed while downloading.
- private static DecimalFormat SIZE_FORMAT = new DecimalFormat("#,##0.##"); //$NON-NLS-1$
// The default chunk size of the buffer used during downloading files.
private static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
+ // The formatter used to format the size displayed while downloading.
+ private static final DecimalFormat SIZE_FORMAT = new DecimalFormat("#,##0.##"); //$NON-NLS-1$
+
+ // The singleton instance.
+ private static CacheManager instance;
+
+ // The time stamp for each file.
+ private Map<URL, Long> timestamps;
+
/**
* Get the singleton cache manager.
*
@@ -58,32 +81,133 @@ public class CacheManager {
}
/**
+ * Create a cache manager.
+ */
+ private CacheManager() {
+ loadTimestamps();
+ }
+
+ /**
+ * If the option of "autosaving" is set to on.
+ *
+ * @return true if it is auto saving or else false.
+ */
+ public boolean isAutoSaving(){
+ IPreferenceStore preferenceStore = UIPlugin.getDefault().getPreferenceStore();
+ boolean autoSaving = preferenceStore.getBoolean(TargetExplorerPreferencePage.PREF_AUTOSAVING);
+ return autoSaving;
+ }
+
+ /**
+ * Load the time stamps from the time stamps file in the cache's root directory.
+ */
+ private void loadTimestamps() {
+ timestamps = Collections.synchronizedMap(new HashMap<URL, Long>());
+ File location = getCacheRoot();
+ File tsFile = new File(location, TIMESTAMP_FILE);
+ if (tsFile.exists()) {
+ Properties properties = new Properties();
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(new FileInputStream(tsFile));
+ properties.loadFromXML(input);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ Enumeration<String> keys = (Enumeration<String>) properties.propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ String value = properties.getProperty(key);
+ long timestamp = 0L;
+ try {
+ timestamp = Long.parseLong(value);
+ timestamps.put(new URL(key), Long.valueOf(timestamp));
+ } catch (Exception nfe) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Save the time stamps to the time stamps file.
+ */
+ private void saveTimestamps(){
+ Properties properties = new Properties();
+ for(URL key:timestamps.keySet()){
+ Long timestamp = timestamps.get(key);
+ properties.setProperty(key.toString(), timestamp.toString());
+ }
+ File location = getCacheRoot();
+ File fTimestamp = new File(location, TIMESTAMP_FILE);
+ OutputStream output = null;
+ try{
+ output = new BufferedOutputStream(new FileOutputStream(fTimestamp));
+ properties.storeToXML(output, null);
+ }catch(IOException e){
+ e.printStackTrace();
+ }finally{
+ if(output!=null){
+ try{
+ output.close();
+ }catch(Exception e){}
+ }
+ }
+ }
+
+ /**
+ * Set the time stamp of the FSTreeNode with the specified location.
+ * @param url The FSTreeNode's location URL.
+ * @param timestamp The new base time stamp to be set.
+ */
+ public void setBaseTimestamp(URL url, long timestamp){
+ timestamps.put(url, Long.valueOf(timestamp));
+ // Persist as well.
+ saveTimestamps();
+ }
+
+ /**
+ * Remove the time stamp entry with the specified URL.
+ * @param url The URL key.
+ */
+ public void removeBaseTimestamp(URL url){
+ timestamps.remove(url);
+ // Persist
+ saveTimestamps();
+ }
+
+ /**
+ * Get the time stamp of the FSTreeNode with the specified location.
+ *
+ * @param url The FSTreeNode's location URL.
+ * @return The FSTreeNode's base time stamp.
+ */
+ public long getBaseTimestamp(URL url){
+ Long timestamp = timestamps.get(url);
+ return timestamp == null ? 0L : timestamp.longValue();
+ }
+
+ /**
* 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 plugin is loaded in a RCP workspace-less environment, the
- * fallback strategy is to use the users home directory.
+ * 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 IPath getCachePath(FSTreeNode node) {
- File location;
- try {
- location = UIPlugin.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
- if (!location.exists()) location.mkdir();
-
+ File location = getCacheRoot();
String agentId = node.peerNode.getPeer().getID();
// Use Math.abs to avoid negative hash value.
String agent = WS_AGENT_DIR_PREFIX + Math.abs(agentId.hashCode());
@@ -92,11 +216,49 @@ public class CacheManager {
if (!agentDirFile.exists()) {
agentDirFile.mkdir();
}
-
return appendNodePath(agentDir, node);
}
/**
+ * 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 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.
+ */
+ private File getCacheRoot() {
+ File location;
+ try {
+ location = UIPlugin.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
+ if (!location.exists()) location.mkdir();
+ return location;
+ }
+
+ /**
* Append the path with the specified node's context path.
*
* @param path
@@ -106,7 +268,7 @@ public class CacheManager {
* @return The path to the node.
*/
private IPath appendNodePath(IPath path, FSTreeNode node) {
- if (!node.isRoot()) {
+ if (!node.isRoot() && node.parent!=null) {
path = appendNodePath(path, node.parent);
return appendPathSegment(node, path, node.name);
}
@@ -124,7 +286,7 @@ public class CacheManager {
* @param node The file/folder node.
* @param path The path to appended.
* @param name The segment's name.
- * @return
+ * @return The path with the segment "name" appended.
*/
private IPath appendPathSegment(FSTreeNode node, IPath path, String name) {
IPath newPath = path.append(name);
@@ -136,75 +298,116 @@ public class CacheManager {
}
/**
- * Get the cache file's staleness. If the cache file does not exist or the
- * file is already out-dated, then return true.
- * <p>
- * When a local cache file is created, the modified time is set to that of
- * its remote corresponding file. So if its remote file is changed, the
- * modified time stamp should be different from the local cache file's
- * modified time stamp.
- * <p>
- * See the method "download" for more details.
- * <p>
- *
- * @param node
- * The file/folder node.
- * @return true if the file doesn't exist or it is stale.
- */
- public boolean isCacheStale(FSTreeNode node) {
- IPath path = getCachePath(node);
- File file = path.toFile();
- return !file.exists() || file.lastModified() != node.attr.mtime;
- }
-
- /**
* Download the data of the file from the remote file system.
- *
+ * Must be called within a UI thread.
* @param node
* The file node.
- * @param parent
- * The shell parent used to display messages.
*
* @return true if it is successful, false there're errors or it is
* canceled.
*/
- public boolean download(final FSTreeNode node, Shell parent) {
+ public boolean download(final FSTreeNode node) {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask(NLS.bind(Messages.CacheManager_DowloadingFile, node.name), 100);
+ OutputStream output = null;
try {
- boolean canceled = downloadFile(node, monitor);
- if (canceled)
+ // Write the data to its local cache file.
+ File file = getCachePath(node).toFile();
+ if(file.exists() && !file.canWrite()){
+ // If the file exists and is read-only, delete it.
+ file.delete();
+ }
+ output = new BufferedOutputStream(new FileOutputStream(file));
+ download2OutputStream(node, output, monitor);
+ if (monitor.isCanceled())
throw new InterruptedException();
} catch (IOException e) {
throw new InvocationTargetException(e);
} finally {
+ if (output != null) {
+ try {
+ output.close();
+ } catch (Exception e) {
+ }
+ }
monitor.done();
}
}
};
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
TimeTriggeredProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(
parent, 250);
dialog.setCancelable(true);
File file = getCachePath(node).toFile();
try {
dialog.run(true, true, runnable);
- // If downloading is successful, set the last modified time to
- // that of its corresponding file.
- file.setLastModified(node.attr.mtime);
+ // If downloading is successful, update the attributes of the file and
+ // set the last modified time to that of its corresponding file.
+ StateManager.getInstance().updateState(node);
// If the node is read-only, make the cache file read-only.
if(!node.isWritable())
file.setReadOnly();
return true;
+ } catch(TCFException e) {
+ MessageDialog.openError(parent, Messages.StateManager_UpdateFailureTitle, e.getLocalizedMessage());
} catch (InvocationTargetException e) {
// Something's gone wrong. Roll back the downloading and display the
// error.
file.delete();
- displayError(node, parent, e);
+ removeBaseTimestamp(node.getLocationURL());
+ displayError(parent, e);
} catch (InterruptedException e) {
// It is canceled. Just roll back the downloading result.
file.delete();
+ removeBaseTimestamp(node.getLocationURL());
+ }
+ return false;
+ }
+
+ /**
+ * Upload the local files to the remote file system.
+ * Must be called within UI thread.
+ * @param nodes
+ * The files' location. Not null.
+ *
+ * @return true if it is successful, false there're errors or it is
+ * canceled.
+ */
+ public boolean upload(final FSTreeNode... nodes) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ try {
+ IRunnableWithProgress runnable = new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ String message;
+ if(nodes.length==1)
+ message = NLS.bind(Messages.CacheManager_UploadSingleFile, nodes[0].name);
+ else
+ message = NLS.bind(Messages.CacheManager_UploadNFiles, Long.valueOf(nodes.length));
+ monitor.beginTask(message, 100);
+ boolean canceled = uploadFiles(monitor, nodes);
+ if (canceled)
+ throw new InterruptedException();
+ } catch (Exception e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ };
+ TimeTriggeredProgressMonitorDialog dialog = new TimeTriggeredProgressMonitorDialog(parent, 250);
+ dialog.setCancelable(true);
+ dialog.run(true, true, runnable);
+ return true;
+ } catch (InvocationTargetException e) {
+ // Something's gone wrong. Roll back the downloading and display the
+ // error.
+ displayError(parent, e);
+ } catch (InterruptedException e) {
+ // It is canceled. Just roll back the downloading result.
}
return false;
}
@@ -219,40 +422,114 @@ public class CacheManager {
* @param e
* The error exception.
*/
- private void displayError(FSTreeNode node, Shell parent, InvocationTargetException e) {
+ private void displayError(Shell parent, InvocationTargetException e) {
Throwable target = e.getTargetException();
Throwable cause = target.getCause() != null ? target.getCause() : target;
MessageDialog.openError(parent, Messages.CacheManager_DownloadingError, cause.getLocalizedMessage());
}
/**
- * Download the specified file using the monitor to report the progress.
+ * Upload the specified files using the monitor to report the progress.
+ *
+ * @param peers
+ * The local files' peer files.
+ * @param locals
+ * The local files to be uploaded.
+ * @param monitor
+ * The monitor used to report the progress.
+ * @return true if it is canceled or else false.
+ * @throws Exception
+ * an Exception thrown during downloading and storing data.
+ */
+ public boolean uploadFiles(IProgressMonitor monitor, FSTreeNode... nodes) throws IOException {
+ BufferedInputStream input = null;
+ BufferedOutputStream output = null;
+ // The buffer used to download the file.
+ byte[] data = new byte[DEFAULT_CHUNK_SIZE];
+ // Calculate the total size.
+ long totalSize = 0;
+ for (FSTreeNode node : nodes) {
+ File file = getCachePath(node).toFile();
+ 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 < nodes.length && !monitor.isCanceled(); i++) {
+ File file = getCachePath(nodes[i]).toFile();
+ try {
+ URL url = nodes[i].getLocationURL();
+ TcfURLConnection connection = (TcfURLConnection) url.openConnection();
+ connection.setDoInput(false);
+ connection.setDoOutput(true);
+ 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.CacheManager_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(!monitor.isCanceled()){
+ // Once upload is successful, synchronize the modified time.
+ try {
+ StateManager.getInstance().commitState(nodes[i]);
+ } catch (TCFException tcfe) {
+ throw new IOException(tcfe.getLocalizedMessage());
+ }
+ }
+ }
+ }
+ return monitor.isCanceled();
+ }
+
+ /**
+ * Download the specified file into an output stream using the monitor to report the progress.
*
* @param node
* The file to be downloaded.
+ * @param output
+ * The output stream.
* @param monitor
* The monitor used to report the progress.
- * @return true if it is canceled or else false.
* @throws IOException
* an IOException thrown during downloading and storing data.
*/
- protected boolean downloadFile(FSTreeNode node, IProgressMonitor monitor)
- throws IOException {
- BufferedInputStream input = null;
- BufferedOutputStream output = null;
- try {
- // Open the input stream of the node using the tcf stream protocol.
+ public void download2OutputStream(FSTreeNode node, OutputStream output, IProgressMonitor monitor) throws IOException {
+ 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);
- // Write the data to its local cache file.
- File file = getCachePath(node).toFile();
- if(file.exists() && !file.canWrite()){
- // If the file exists and is read-only, delete it.
- file.delete();
- }
- output = new BufferedOutputStream(new FileOutputStream(file));
-
// The buffer used to download the file.
byte[] data = new byte[DEFAULT_CHUNK_SIZE];
// Calculate the chunk size of one percent.
@@ -277,14 +554,7 @@ public class CacheManager {
}
}
}
- return monitor.isCanceled();
- } finally {
- if (output != null) {
- try {
- output.close();
- } catch (Exception e) {
- }
- }
+ }finally{
if (input != null) {
try {
input.close();
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java
new file mode 100644
index 000000000..1e8d96ebc
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CachePropertyTester.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.core.expressions.PropertyTester;
+
+/**
+ * Provide a tester to test if the current auto saving mode is on or off.
+ *
+ */
+public class CachePropertyTester extends PropertyTester {
+ /**
+ * Create a cache property tester.
+ */
+ public CachePropertyTester() {
+ }
+
+ /* (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(property.equals("isAutoSavingOn")){ //$NON-NLS-1$
+ return CacheManager.getInstance().isAutoSaving();
+ }
+ return false;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java
new file mode 100644
index 000000000..6ece7624c
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/CommitHandler.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import java.io.File;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler that commits the content of a modified file to the target file system.
+ */
+public class CommitHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ try {
+ // Refresh the node to get the latest state.
+ StateManager.getInstance().refreshState(node);
+ Shell parent = HandlerUtil.getActiveShell(event);
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if (file.exists()) {
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case conflict:
+ String title = Messages.CmmitHandler_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.CmmitHandler_StateChangedMessage, node.name);
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message, MessageDialog.QUESTION, new String[] { Messages.CmmitHandler_Merge, Messages.CmmitHandler_CommitAnyway,
+ Messages.CmmitHandler_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) {// Merge
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ IWorkbenchPage page = HandlerUtil.getActiveSite(event).getPage();
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ } else if (index == 1) { // Commit anyway
+ CacheManager.getInstance().upload(node);
+ }
+ break;
+ case modified:
+ // Commit it normally.
+ CacheManager.getInstance().upload(node);
+ break;
+ case consistent:
+ break;
+ case outdated:
+ break;
+ }
+ } else {
+ String message = NLS.bind(Messages.CmmitHandler_FileDeleted, file.getName());
+ MessageDialog.openError(parent, Messages.CmmitHandler_ErrorTitle, message);
+ }
+ } catch (TCFException e) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ }
+ return null;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java
index 0ae5dcf57..a746b982e 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/ContentTypeHelper.java
@@ -9,9 +9,9 @@
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -38,10 +38,10 @@ public class ContentTypeHelper {
private Map<URL, IContentType> resolvables = new HashMap<URL, IContentType>();
// Already known unresolvable file nodes specified by their URLs.
private Map<URL, FSTreeNode> unresolvables = new HashMap<URL, FSTreeNode>();
-
+
/**
* Get the singleton instance of the content type helper.
- *
+ *
* @return The singleton instance of the content type helper.
*/
public static ContentTypeHelper getInstance() {
@@ -53,7 +53,7 @@ public class ContentTypeHelper {
/**
* 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.
@@ -71,24 +71,19 @@ public class ContentTypeHelper {
/**
* Get the content type of the specified file node.
- *
+ *
* @param node
* The file node.
* @return The content type of the file node.
*/
public IContentType getContentType(FSTreeNode node) {
- URL location = null;
- try {
- location = node.getLocationURL();
- } catch (MalformedURLException e1) {
- return null;
- }
+ URL location = node.getLocationURL();
if (unresolvables.get(location) != null)
// If it is already known unresolvable.
return null;
IContentType contentType = null;
contentType = resolvables.get(location);
- if (contentType != null)
+ if (contentType != null)
// If it is already known to have a certain content type.
return contentType;
// First check the content type by its name.
@@ -110,7 +105,7 @@ public class ContentTypeHelper {
/**
* Find the content type of the file using its content stream.
- *
+ *
* @param node
* The file node.
* @return The content type of the file.
@@ -119,13 +114,12 @@ public class ContentTypeHelper {
* @throws IOException
* If something goes wrong during the content type parsing.
*/
- private IContentType findContentTypeByStream(FSTreeNode node)
- throws CoreException, IOException {
+ private IContentType findContentTypeByStream(FSTreeNode node) throws CoreException, IOException {
InputStream is = null;
try {
- if (!CacheManager.getInstance().isCacheStale(node)) {
- // If the local cache file is update to date, then use the local
- // cache file.
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if (file.exists()) {
+ // If the local cache file exits.
IPath path = CacheManager.getInstance().getCachePath(node);
IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
is = fileStore.openInputStream(EFS.NONE, null);
@@ -134,8 +128,7 @@ public class ContentTypeHelper {
URL url = node.getLocationURL();
is = url.openStream();
}
- return Platform.getContentTypeManager().findContentTypeFor(is,
- node.name);
+ return Platform.getContentTypeManager().findContentTypeFor(is, node.name);
} finally {
if (is != null) {
try {
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java
index 0e23f0a1b..4e7ada41c 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/FSTreeNodePropertyTester.java
@@ -12,14 +12,18 @@
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+import java.io.File;
+
import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
import org.eclipse.tm.te.tcf.filesystem.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 and "isExecutable" if it is executable.
+ * it is writable, "isExecutable" if it is executable and "getCacheState" to
+ * get a node's state.
*/
public class FSTreeNodePropertyTester extends PropertyTester {
@@ -42,6 +46,12 @@ public class FSTreeNodePropertyTester extends PropertyTester {
return node.isWritable();
} else if (property.equals("isExecutable")){ //$NON-NLS-1$
return node.isExecutable();
+ } else if (property.equals("getCacheState")){ //$NON-NLS-1$
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if(!file.exists())
+ return false;
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ return state.name().equals(expectedValue);
}
return false;
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java
new file mode 100644
index 000000000..08d3b5cb6
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/MergeHandler.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler used to merge a file which is conflicting with its remote file.
+ */
+public class MergeHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ return null;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java
index 00214c396..b92ee3f39 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/OpenFileHandler.java
@@ -9,6 +9,9 @@
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.internal.handlers;
+import java.io.File;
+
+import org.eclipse.compare.CompareUI;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
@@ -17,9 +20,15 @@ import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
@@ -36,13 +45,14 @@ public class OpenFileHandler extends AbstractHandler {
*/
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getActiveMenuSelectionChecked(event);
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
final FSTreeNode node = (FSTreeNode) selection.getFirstElement();
IWorkbenchPage page = HandlerUtil.getActiveSite(event).getPage();
if (ContentTypeHelper.getInstance().isBinaryFile(node)) {
// If the file is a binary file.
Shell parent = HandlerUtil.getActiveShell(event);
- MessageDialog.openWarning(parent, Messages.OpenFileHandler_Warning, Messages.OpenFileHandler_OpeningBinaryNotSupported);
+ MessageDialog.openWarning(parent, Messages.OpenFileHandler_Warning,
+ Messages.OpenFileHandler_OpeningBinaryNotSupported);
} else {
// Open the file node.
openFile(node, page);
@@ -61,13 +71,63 @@ public class OpenFileHandler extends AbstractHandler {
* The workbench page in which the editor is opened.
*/
private void openFile(FSTreeNode node, IWorkbenchPage page) {
- if (CacheManager.getInstance().isCacheStale(node)) {
- // If the file node's local cache is already stale, download it.
- Shell parent = page.getWorkbenchWindow().getShell();
- boolean successful = CacheManager.getInstance().download(node, parent);
- if (successful) openEditor(page, node);
- } else {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if (!file.exists()) {
+ // If the file node's local cache does not exist yet, download it.
+ boolean successful = CacheManager.getInstance().download(node);
+ if (!successful) {
+ return;
+ }
+ }
+ if (!CacheManager.getInstance().isAutoSaving()) {
openEditor(page, node);
+ } else {
+ try {
+ StateManager.getInstance().refreshState(node);
+ } catch (TCFException e) {
+ Shell parent = page.getWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ return;
+ }
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case consistent:
+ openEditor(page, node);
+ break;
+ case modified: {
+ // If the file node's local cache has been modified, upload it
+ // before open it.
+ boolean successful = CacheManager.getInstance().upload(node);
+ if (successful)
+ openEditor(page, node);
+ }
+ break;
+ case outdated: {
+ // If the file node's local cache does not exist yet, download
+ // it.
+ boolean successful = CacheManager.getInstance().download(node);
+ if (successful)
+ openEditor(page, node);
+ }
+ break;
+ case conflict: {
+ String title = Messages.OpenFileHandler_ConflictingTitle;
+ String message = NLS.bind(Messages.OpenFileHandler_ConflictingMessage, node.name);
+ Shell parent = page.getWorkbenchWindow().getShell();
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message, MessageDialog.QUESTION, new String[] { Messages.OpenFileHandler_Merge, Messages.OpenFileHandler_OpenAnyway,
+ Messages.OpenFileHandler_Cancel }, 0);
+ int index = msgDialog.open();
+ if (index == 0) {
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ } else if (index == 1) {
+ openEditor(page, node);
+ }
+ }
+ break;
+ }
}
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java
new file mode 100644
index 000000000..bb7ec71f7
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RefreshHandler.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler to refresh the state of a file.
+ */
+public class RefreshHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ try {
+ StateManager.getInstance().refreshState(node);
+ } catch (TCFException e) {
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ }
+ return null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java
new file mode 100644
index 000000000..83e27c185
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/RevertHandler.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler to revert the content of the file from the remote file.
+ * This handler is enabled only when the file is modified or conflicting.
+ */
+public class RevertHandler extends AbstractHandler {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ CacheManager.getInstance().download(node);
+ return null;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java
new file mode 100644
index 000000000..e43b4c7fd
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/StateManager.java
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import java.io.File;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneSetStat;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneStat;
+import org.eclipse.tm.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.TCFUtilities;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
+/**
+ * This class provides several utility methods to get, update, commit
+ * or refresh a file node's state.
+ *
+ */
+public class StateManager {
+
+ // The singleton instance.
+ private static StateManager instance;
+
+ /**
+ * Get the singleton user manager.
+ *
+ * @return The singleton cache manager.
+ */
+ public static StateManager getInstance() {
+ if (instance == null) {
+ instance = new StateManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Create a StateManager fInstance.
+ */
+ private StateManager() {
+ }
+
+ /**
+ * Update the state of the specified node.
+ *
+ * @param node The tree node whose state is going to be updated.
+ * @throws TCFException
+ */
+ public void updateState(FSTreeNode node) throws TCFException {
+ updateFileStat(node, true);
+ }
+
+ /**
+ * Refresh the state of the specified node.
+ *
+ * @param node The tree node whose state is going to be refreshed.
+ * @throws TCFException
+ */
+ public void refreshState(FSTreeNode node) throws TCFException {
+ updateFileStat(node, false);
+ }
+
+ /**
+ * Update the file's state of the specified tree node. Synchronize the time stamp of
+ * the file with the base time stamp and that of the remote file if sync is true.
+ *
+ * @param node The tree node whose file state is going to be updated.
+ * @param sync If its base time stamp and its remote file's time stamp should be synchronized.
+ */
+ private void updateFileStat(final FSTreeNode node, final boolean sync) throws TCFException {
+ IChannel channel = null;
+ try {
+ channel = TCFUtilities.openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ updateFileAttr(node, sync, channel);
+ }
+ } finally {
+ if (channel != null)
+ channel.close();
+ }
+ }
+
+ private void updateFileAttr(final FSTreeNode node, final boolean sync, IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ String path = node.getLocation(true);
+ service.stat(path, new DoneStat() {
+ @Override
+ public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) {
+ if (error == null) {
+ updateNodeAttr(node, attrs, sync);
+ } else {
+ String message = NLS.bind(Messages.StateManager_CannotGetFileStatMessage, new Object[]{node.name, error});
+ errors[0] = new TCFFileSystemException(message, error);
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.StateManager_CannotGetFileStateMessage2, new Object[]{node.name, e});
+ errors[0] = new TCFFileSystemException(message, e);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ }else{
+ String message = NLS.bind(Messages.StateManager_TCFNotProvideFSMessage, node.peerNode.getPeer().getID());
+ throw new TCFFileSystemException(message);
+ }
+ }
+
+ /**
+ * Update the file attribute of the specified tree node with the specified value
+ * and synchronize its base timestamp and its remote file's timestamp if "sync" is true.
+ *
+ * @param node The tree node whose file attribute is to updated.
+ * @param attr The new file attribute.
+ * @param sync If the timestamps should be synchronized.
+ */
+ void updateNodeAttr(FSTreeNode node, FileAttrs attr, boolean sync){
+ node.attr = attr;
+ if (sync) {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ assert file.exists();
+ file.setLastModified(attr.mtime);
+ CacheManager.getInstance().setBaseTimestamp(node.getLocationURL(), attr.mtime);
+ }
+ FSModel.getInstance().fireNodeStateChanged(node);
+ }
+
+ /**
+ * Commit the content of the local file to the target.
+ *
+ * @param node The tree node whose local file is going to committed.
+ * @throws TCFException
+ */
+ public void commitState(final FSTreeNode node) throws TCFException {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ assert file.exists();
+ long mtime = file.lastModified();
+ // Create the new file attribute based on the file's last modified time.
+ final IFileSystem.FileAttrs attrs = new IFileSystem.FileAttrs(node.attr.flags, node.attr.size, node.attr.uid, node.attr.gid, node.attr.permissions, node.attr.atime, mtime,
+ node.attr.attributes);
+ IChannel channel = null;
+ try {
+ channel = TCFUtilities.openChannel(node.peerNode.getPeer());
+ if (channel != null) {
+ commitFileAttr(node, attrs, channel);
+ }
+ } finally {
+ if (channel != null)
+ channel.close();
+ }
+ }
+
+ private void commitFileAttr(final FSTreeNode node, final IFileSystem.FileAttrs attrs, IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ String path = node.getLocation(true);
+ service.setstat(path, attrs, new DoneSetStat() {
+ @Override
+ public void doneSetStat(IToken token, FileSystemException error) {
+ if (error == null) {
+ commitNodeAttr(node, attrs);
+ } else {
+ String message = NLS.bind(Messages.StateManager_CannotSetFileStateMessage, new Object[] { node.name, error });
+ errors[0] = new TCFFileSystemException(message, error);
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.StateManager_CannotSetFileStateMessage2, new Object[] { node.name, e });
+ errors[0] = new TCFFileSystemException(message, e);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ } else {
+ String message = NLS.bind(Messages.StateManager_TCFNotProvideFSMessage2, node.peerNode.getPeer().getID());
+ throw new TCFFileSystemException(message);
+ }
+ }
+
+ /**
+ * Commit the file attribute of the specified tree node with the specified value.
+ *
+ * @param node The tree node whose file attribute is to committed.
+ * @param attr The new file attribute.
+ */
+ void commitNodeAttr(FSTreeNode node, FileAttrs attr){
+ node.attr = attr;
+ CacheManager.getInstance().setBaseTimestamp(node.getLocationURL(), attr.mtime);
+ FSModel.getInstance().fireNodeStateChanged(node);
+ }
+
+ /**
+ * 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(FSTreeNode node) {
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if(!file.exists())
+ return CacheState.consistent;
+ long ltime = file.lastModified();
+ long btime = CacheManager.getInstance().getBaseTimestamp(node.getLocationURL());
+ long mtime = 0;
+ if(node.attr!=null)
+ mtime = node.attr.mtime;
+ if(btime == ltime){
+ if(isUnchanged(mtime, btime))
+ return CacheState.consistent;
+ return CacheState.outdated;
+ }
+ if(isUnchanged(mtime, btime))
+ return CacheState.modified;
+ return CacheState.conflict;
+ }
+
+ /**
+ * Compare the modified time of the remote file and the base timestamp
+ * and see if they are equal to each other.
+ *
+ * @param mtime The modified time of the remote file.
+ * @param btime The base timestamp cached.
+ * @return true if they are equal in second precision.
+ */
+ private boolean isUnchanged(long mtime, long btime){
+ return Math.abs(mtime-btime)/1000 == 0;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java
new file mode 100644
index 000000000..56f367a19
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UpdateHandler.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import java.io.File;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.LocalTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.MergeEditorInput;
+import org.eclipse.tm.te.tcf.filesystem.internal.compare.RemoteTypedElement;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.CacheState;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The handler to update the local file's content with the latest of its remote file.
+ *
+ */
+public class UpdateHandler extends AbstractHandler {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getCurrentSelectionChecked(event);
+ FSTreeNode node = (FSTreeNode) selection.getFirstElement();
+ try {
+ StateManager.getInstance().refreshState(node);
+ } catch (TCFException e) {
+ Shell parent = HandlerUtil.getActiveShell(event);
+ MessageDialog.openError(parent, Messages.StateManager_RefreshFailureTitle, e.getLocalizedMessage());
+ return null;
+ }
+ Shell parent = HandlerUtil.getActiveShell(event);
+ File file = CacheManager.getInstance().getCacheFile(node);
+ if(file.exists()){
+ CacheState state = StateManager.getInstance().getCacheState(node);
+ switch (state) {
+ case conflict:
+ String title = Messages.UpdateHandler_StateChangedDialogTitle;
+ String message = NLS.bind(Messages.UpdateHandler_StateChangedMessage, node.name);
+ MessageDialog msgDialog = new MessageDialog(parent, title, null, message,
+ MessageDialog.QUESTION, new String[]{Messages.UpdateHandler_Merge,
+ Messages.UpdateHandler_UpdateAnyway, Messages.UpdateHandler_Cancel}, 0);
+ int index = msgDialog.open();
+ if (index == 0) {
+ LocalTypedElement local = new LocalTypedElement(node);
+ RemoteTypedElement remote = new RemoteTypedElement(node);
+ IWorkbenchPage page = HandlerUtil.getActiveSite(event).getPage();
+ MergeEditorInput input = new MergeEditorInput(local, remote, page);
+ CompareUI.openCompareDialog(input);
+ }else if(index == 1){
+ CacheManager.getInstance().download(node);
+ }
+ break;
+ case modified:
+ break;
+ case consistent:
+ break;
+ case outdated:
+ CacheManager.getInstance().download(node);
+ break;
+ }
+ }else{
+ CacheManager.getInstance().download(node);
+ }
+ return null;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java
new file mode 100644
index 000000000..826f1b20a
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/handlers/UserManager.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.handlers;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneUser;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.UserAccount;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFFileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous;
+import org.eclipse.tm.te.tcf.filesystem.internal.url.TCFUtilities;
+import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A facility class to retrieve the user's information for a target file system.
+ */
+public class UserManager {
+ // The key to save and retrieve the user account in a peer model.
+ private static final String USER_ACCOUNT_KEY = "user.account"; //$NON-NLS-1$
+
+ // The singleton fInstance.
+ private static UserManager instance;
+
+ /**
+ * Get the singleton user manager.
+ *
+ * @return The singleton cache manager.
+ */
+ public static UserManager getInstance() {
+ if (instance == null) {
+ instance = new UserManager();
+ }
+ return instance;
+ }
+
+ private UserManager() {
+ }
+
+ /**
+ * 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.
+ */
+ private UserAccount getUserByChannel(final IChannel channel) throws TCFFileSystemException {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final TCFFileSystemException[] errors = new TCFFileSystemException[1];
+ final Rendezvous rendezvous = new Rendezvous();
+ 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 {
+ String message = NLS.bind(Messages.UserManager_CannotGetUserAccountMessage, channel.getRemotePeer().getID());
+ errors[0] = new TCFFileSystemException(message, error);
+ }
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.UserManager_CannotGetUserAccountMessage2, channel.getRemotePeer().getID());
+ errors[0] = new TCFFileSystemException(message, e);
+ }
+ if (errors[0] != null) {
+ throw errors[0];
+ }
+ return accounts[0];
+ }
+ String message = NLS.bind(Messages.UserManager_TCFNotProvideFSMessage, channel.getRemotePeer().getID());
+ throw new TCFFileSystemException(message);
+ }
+
+ /**
+ * Get the information of the client user account.
+ *
+ * @return The client user account's information.
+ */
+ public UserAccount getUserAccount(IPeerModel peerNode) {
+ UserAccount account = getUserFromPeer(peerNode);
+ if (account == null) {
+ IChannel channel = null;
+ try{
+ channel = TCFUtilities.openChannel(peerNode.getPeer());
+ if (channel != null) {
+ account = getUserByChannel(channel);
+ if (account != null)
+ setUserToPeer(peerNode, account);
+ }
+ }catch(TCFException e){
+ Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(parent, Messages.UserManager_UserAccountTitle, e.getLocalizedMessage());
+ }finally{
+ if(channel!=null){
+ channel.close();
+ }
+ }
+ }
+ return account;
+ }
+
+ /**
+ * 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 IPeerModel 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.
+ */
+ private void setUserToPeer(final IPeerModel peer, final UserAccount account) {
+ assert peer != null && account != null;
+ 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.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java
index c6ba5634a..787822b58 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.java
@@ -103,6 +103,16 @@ public class Messages extends NLS {
public static String CacheManager_DownloadingProgress;
public static String CacheManager_KBs;
public static String CacheManager_MBs;
+ public static String CacheManager_UploadingProgress;
+ public static String CacheManager_UploadNFiles;
+ public static String CacheManager_UploadSingleFile;
+ public static String CmmitHandler_Cancel;
+ public static String CmmitHandler_CommitAnyway;
+ public static String CmmitHandler_ErrorTitle;
+ public static String CmmitHandler_FileDeleted;
+ public static String CmmitHandler_Merge;
+ public static String CmmitHandler_StateChangedDialogTitle;
+ public static String CmmitHandler_StateChangedMessage;
public static String InformationPage_Accessed;
public static String InformationPage_Advanced;
public static String InformationPage_Attributes;
@@ -124,7 +134,13 @@ public class Messages extends NLS {
public static String FSTreeControl_column_modified_label;
public static String FSOpenFileDialog_title;
+ public static String LocalTypedElement_SavingFile;
+ public static String OpenFileHandler_Cancel;
+ public static String OpenFileHandler_ConflictingMessage;
+ public static String OpenFileHandler_ConflictingTitle;
+ public static String OpenFileHandler_Merge;
+ public static String OpenFileHandler_OpenAnyway;
public static String OpenFileHandler_OpeningBinaryNotSupported;
public static String OpenFileHandler_Warning;
public static String PermissionsGroup_Executable;
@@ -133,6 +149,26 @@ public class Messages extends NLS {
public static String PermissionsGroup_Readable;
public static String PermissionsGroup_UserPermissions;
public static String PermissionsGroup_Writable;
+ public static String RemoteTypedElement_GettingRemoteContent;
+ public static String SaveAllListener_Cancel;
+ public static String SaveAllListener_Merge;
+ public static String SaveAllListener_SaveAnyway;
+ public static String SaveAllListener_SingularMessage;
+ public static String SaveAllListener_StateChangedDialogTitle;
+ public static String SaveListener_Cancel;
+ public static String SaveListener_Merge;
+ public static String SaveListener_SaveAnyway;
+ public static String SaveListener_StateChangedDialogTitle;
+ public static String SaveListener_StateChangedMessage;
+ public static String StateManager_CannotGetFileStateMessage2;
+ public static String StateManager_CannotGetFileStatMessage;
+ public static String StateManager_CannotSetFileStateMessage;
+ public static String StateManager_CannotSetFileStateMessage2;
+ public static String StateManager_CommitFailureTitle;
+ public static String StateManager_RefreshFailureTitle;
+ public static String StateManager_TCFNotProvideFSMessage;
+ public static String StateManager_TCFNotProvideFSMessage2;
+ public static String StateManager_UpdateFailureTitle;
public static String TcfInputStream_CloseTimeout;
public static String TcfInputStream_NoDataAvailable;
public static String TcfInputStream_NoFileReturned;
@@ -141,4 +177,23 @@ public class Messages extends NLS {
public static String TcfInputStream_OpenTCFTimeout;
public static String TcfInputStream_ReadTimeout;
public static String TcfInputStream_StreamClosed;
+ public static String TcfOutputStream_StreamClosed;
+ public static String TcfOutputStream_WriteTimeout;
+ public static String TcfURLConnection_CloseFileTimeout;
+ public static String TcfURLConnection_NoFileHandleReturned;
+ public static String TcfURLConnection_NoFSServiceAvailable;
+ public static String TcfURLConnection_NoSuchTcfAgent;
+ public static String TcfURLConnection_OpenFileTimeout;
+ public static String TcfURLConnection_OpenTCFChannelTimeout;
+ public static String TCFUtilities_OpeningFailureMessage;
+ public static String TCFUtilities_OpeningFailureTitle;
+ public static String UpdateHandler_Cancel;
+ public static String UpdateHandler_Merge;
+ public static String UpdateHandler_StateChangedDialogTitle;
+ public static String UpdateHandler_StateChangedMessage;
+ public static String UpdateHandler_UpdateAnyway;
+ public static String UserManager_CannotGetUserAccountMessage;
+ public static String UserManager_CannotGetUserAccountMessage2;
+ public static String UserManager_TCFNotProvideFSMessage;
+ public static String UserManager_UserAccountTitle;
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties
index b5d362d6c..08e539479 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/nls/Messages.properties
@@ -57,6 +57,22 @@ CacheManager_DownloadingError=Downloading Error
CacheManager_DownloadingProgress=Downloading {0}/{1}.
CacheManager_KBs=\ KBs
CacheManager_MBs=\ MBs
+CacheManager_UploadingProgress=Uploading file {0}: {1}/{2}
+CacheManager_UploadNFiles=Uploading {0} files...
+CacheManager_UploadSingleFile=Uploading file {0}...
+CmmitHandler_Cancel=Cancel
+CmmitHandler_CommitAnyway=Commit anyway
+CmmitHandler_ErrorTitle=File Deleted
+CmmitHandler_FileDeleted=The local file {0} that you are trying to commit has been deleted\!
+CmmitHandler_Merge=Merge
+CmmitHandler_StateChangedDialogTitle=State Changed
+CmmitHandler_StateChangedMessage={0} on the target has changed and is conflicting with the local file. What do you want?
+LocalTypedElement_SavingFile=Saving file:
+OpenFileHandler_Cancel=Cancel
+OpenFileHandler_ConflictingMessage=The local {0} is conflicting with the remote version. What do you want?
+OpenFileHandler_ConflictingTitle=Conflicting content
+OpenFileHandler_Merge=Merge
+OpenFileHandler_OpenAnyway=Open anyway
OpenFileHandler_OpeningBinaryNotSupported=Opening a binary file is not supported yet.
OpenFileHandler_Warning=Warning
@@ -66,6 +82,26 @@ PermissionsGroup_OtherPermissions=Other:
PermissionsGroup_Readable=Readable
PermissionsGroup_UserPermissions=User:
PermissionsGroup_Writable=Writable
+RemoteTypedElement_GettingRemoteContent=Getting content from the remote file:
+SaveAllListener_Cancel=Cancel
+SaveAllListener_Merge=Merge
+SaveAllListener_SaveAnyway=Save anyway
+SaveAllListener_SingularMessage=The file {0} on the target has changed and is conflicting with the local file. What do you want?
+SaveAllListener_StateChangedDialogTitle=State Changed
+SaveListener_Cancel=Cancel
+SaveListener_Merge=Merge
+SaveListener_SaveAnyway=Save anyway
+SaveListener_StateChangedDialogTitle=State Changed
+SaveListener_StateChangedMessage={0} on the target has changed and is conflicting with the local file. What do you want?
+StateManager_CannotGetFileStateMessage2=Cannot get the file's stat of {0}\!. Caused by {1}
+StateManager_CannotGetFileStatMessage=Cannot get the file's stat of {0}\!. Caused by {1}
+StateManager_CannotSetFileStateMessage=Cannot set the file's stat of {0}\!. Caused by {1}
+StateManager_CannotSetFileStateMessage2=Cannot set the file's stat of {0}\!. Caused by {1}
+StateManager_CommitFailureTitle=Commit Failure
+StateManager_RefreshFailureTitle=Refresh Failure
+StateManager_TCFNotProvideFSMessage=This TCF agent, {0}, does not provide a file system service\!
+StateManager_TCFNotProvideFSMessage2=This TCF agent, {0}, does not provide a file system service\!
+StateManager_UpdateFailureTitle=Update Failure
TcfInputStream_CloseTimeout=Closing has timed out\!
TcfInputStream_NoDataAvailable=No data available
TcfInputStream_NoFileReturned=No file handle returned\!
@@ -74,3 +110,22 @@ TcfInputStream_OpenFileTimeout=Opening file has timed out\!
TcfInputStream_OpenTCFTimeout=Opening TCF channel has timed out\!
TcfInputStream_ReadTimeout=Reading has timed out\!
TcfInputStream_StreamClosed=Stream is already closed\!
+TcfOutputStream_StreamClosed=Stream is already closed\!
+TcfOutputStream_WriteTimeout=Writing has timed out\!
+TcfURLConnection_CloseFileTimeout=Closing has timed out\!
+TcfURLConnection_NoFileHandleReturned=No file handle returned\!
+TcfURLConnection_NoFSServiceAvailable=No remote File System Service available\!
+TcfURLConnection_NoSuchTcfAgent=TCF agent is already disconnected\!
+TcfURLConnection_OpenFileTimeout=Opening file has timed out\!
+TcfURLConnection_OpenTCFChannelTimeout=Opening TCF channel has timed out\!
+TCFUtilities_OpeningFailureMessage=We cannot open a TCF channel to the target: {0}. It is caused by {1}.
+TCFUtilities_OpeningFailureTitle=Opening Channel
+UpdateHandler_Cancel=Cancel
+UpdateHandler_Merge=Merge
+UpdateHandler_StateChangedDialogTitle=State Changed
+UpdateHandler_StateChangedMessage=The local {0} has changed and is conflicting with the remote file. What do you want?
+UpdateHandler_UpdateAnyway=Update anyway
+UserManager_CannotGetUserAccountMessage=Cannot get the user account from the agent {0}
+UserManager_CannotGetUserAccountMessage2=Cannot get the user account from the agent {0}. Caused by: networking too slow.
+UserManager_TCFNotProvideFSMessage=This TCF agent, {0}, does not provide a file system service\!
+UserManager_UserAccountTitle=User Account
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java
index c8e247ca0..134750f39 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/PreferencesInitializer.java
@@ -12,6 +12,7 @@ package org.eclipse.tm.te.tcf.filesystem.internal.preferences;
import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
import org.eclipse.tm.te.tcf.filesystem.interfaces.preferences.IPreferenceKeys;
@@ -39,5 +40,7 @@ public class PreferencesInitializer extends AbstractPreferenceInitializer {
// [Hidden] Editor content contribution: default on
prefs.putBoolean(IPreferenceKeys.PREF_FEATURE_ENABLE_EDITOR_CONTENT_CONTRIBUTION, true);
}
+ IPreferenceStore preferenceStore = UIPlugin.getDefault().getPreferenceStore();
+ preferenceStore.setDefault(TargetExplorerPreferencePage.PREF_AUTOSAVING, TargetExplorerPreferencePage.DEFAULT_AUTOSAVING);
}
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java
new file mode 100644
index 000000000..a00d965f3
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/preferences/TargetExplorerPreferencePage.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.preferences;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.tm.te.tcf.filesystem.activator.UIPlugin;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+/**
+ * The preference page for configuring the preference options for Target
+ * Explorer File System Explorer.
+ *
+ */
+public class TargetExplorerPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+ // The preference key to access the option of auto saving
+ public static final String PREF_AUTOSAVING = "PrefAutoSaving"; //$NON-NLS-1$
+ // The default value of the option of auto saving.
+ public static final boolean DEFAULT_AUTOSAVING = true;
+
+ // The editor to edit the value of auto saving.
+ protected BooleanFieldEditor fAutoSaving;
+
+ /***
+ * Create a preference page for Target Explorer File System Explorer.
+ */
+ public TargetExplorerPreferencePage() {
+ super(GRID);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.FieldEditorPreferencePage#createFieldEditors()
+ */
+ @Override
+ protected void createFieldEditors() {
+ UIPlugin plugin = UIPlugin.getDefault();
+ IPreferenceStore preferenceStore = plugin.getPreferenceStore();
+ setPreferenceStore(preferenceStore);
+ fAutoSaving = new BooleanFieldEditor(PREF_AUTOSAVING, "Automatically upload files to targets upon saving.", //$NON-NLS-1$
+ getFieldEditorParent());
+ addField(fAutoSaving);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
+ */
+ @Override
+ public void init(IWorkbench workbench) {
+ // do nothing
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java
new file mode 100644
index 000000000..b27f3ed02
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TCFUtilities.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.url;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFChannelException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+
+/**
+ * A utilities class that provides common TCF operations.
+ */
+public class TCFUtilities {
+
+ /**
+ * 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 Rendezvous rendezvous = new Rendezvous();
+ final TCFChannelException[] errors = new TCFChannelException[1];
+ final IChannel[] channels = new IChannel[1];
+ channels[0] = peer.openChannel();
+ channels[0].addChannelListener(new IChannelListener() {
+ @Override
+ public void onChannelOpened() {
+ rendezvous.arrive();
+ }
+
+ @Override
+ public void onChannelClosed(Throwable error) {
+ if (error != null) {
+ String message = NLS.bind(Messages.TCFUtilities_OpeningFailureMessage,
+ new Object[]{peer.getID(), error.getLocalizedMessage()});
+ errors[0] = new TCFChannelException(message, error);
+ rendezvous.arrive();
+ }
+ }
+
+ @Override
+ public void congestionLevel(int level) {
+ }
+ });
+ try {
+ rendezvous.waiting(5000L);
+ } catch (InterruptedException e) {
+ String message = NLS.bind(Messages.TCFUtilities_OpeningFailureMessage,
+ new Object[]{peer.getID(), e.getLocalizedMessage()});
+ errors[0] = new TCFChannelException(message, e);
+ }
+ if(errors[0] != null){
+ throw errors[0];
+ }
+ return channels[0];
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java
index f446ef585..6e41bb15f 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfInputStream.java
@@ -12,18 +12,10 @@ package org.eclipse.tm.te.tcf.filesystem.internal.url;
import java.io.IOException;
import java.io.InputStream;
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.tm.tcf.protocol.IChannel;
-import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener;
-import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.IToken;
-import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IFileSystem;
-import org.eclipse.tm.tcf.services.IFileSystem.DoneClose;
-import org.eclipse.tm.tcf.services.IFileSystem.DoneOpen;
import org.eclipse.tm.tcf.services.IFileSystem.DoneRead;
import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
-import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle;
import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
/**
@@ -34,21 +26,6 @@ public class TcfInputStream extends InputStream {
// Default chunk size while pumping the data.
private static final int DEFAULT_CHUNK_SIZE = 5 * 1024;
- // If the stream has already connected.
- private boolean connected;
-
- // The TCF agent peer.
- IPeer peer;
- // The remote path to the resource.
- String path;
-
- // The TCF channel used to open and read the resource.
- IChannel channel;
- // The file system service used to open and read the resource.
- IFileSystem service;
- // The file's handle
- IFileHandle handle;
-
// Current reading position
long position;
// The byte array used to buffer data.
@@ -66,14 +43,11 @@ public class TcfInputStream extends InputStream {
// The chunk size of the reading buffer.
int chunk_size = 0;
- // Channel opening timeout.
- long connectTimeout;
- // File opening timeout.
- long openTimeout;
// File reading timeout.
- long readTimeout;
- // File closing timeout.
- long closeTimeout;
+ int timeout;
+
+ // The URL Connection
+ TcfURLConnection connection;
/**
* Create a TCF input stream connected the specified peer with specified
@@ -84,8 +58,8 @@ public class TcfInputStream extends InputStream {
* @param path
* The path to the remote resource.
*/
- public TcfInputStream(IPeer peer, String path) {
- this(peer, path, DEFAULT_CHUNK_SIZE);
+ public TcfInputStream(TcfURLConnection connection) {
+ this(connection, DEFAULT_CHUNK_SIZE);
}
/**
@@ -99,112 +73,18 @@ public class TcfInputStream extends InputStream {
* @param chunk_size
* The buffer size.
*/
- public TcfInputStream(IPeer peer, final String path, int chunk_size) {
- this.peer = peer;
- this.path = path;
+ public TcfInputStream(TcfURLConnection connection, int chunk_size) {
+ this.connection = connection;
this.chunk_size = chunk_size;
}
/**
- * Set the timeout for opening a TCF channel.
- *
- * @param connectTimeout the timeout in milliseconds.
- */
- void setConnectTimeout(long channelTimeout) {
- this.connectTimeout = channelTimeout;
- }
-
- /**
- * Set the timeout for opening a file.
- *
- * @param openTimeout the timeout in milliseconds.
- */
- void setOpenTimeout(long openTimeout) {
- this.openTimeout = openTimeout;
- }
-
- /**
* Set the timeout for reading a file.
*
- * @param readTimeout the timeout in milliseconds.
- */
- void setReadTimeout(long readTimeout) {
- this.readTimeout = readTimeout;
- }
-
- /**
- * Set the timeout for closing a file.
- *
- * @param closeTimeout the timeout in milliseconds.
+ * @param timeout the timeout in milliseconds.
*/
- void setCloseTimeout(long closeTimeout) {
- this.closeTimeout = closeTimeout;
- }
-
-
- /**
- * Open and connect the input stream to the agent peer.
- *
- * @throws IOException
- * Thrown when the connecting is failed.
- */
- private void connect() throws IOException {
- final Rendezvous rendezvous = new Rendezvous();
- // Open the channel
- channel = peer.openChannel();
- channel.addChannelListener(new IChannelListener() {
- @Override
- public void onChannelOpened() {
- Assert.isTrue(Protocol.isDispatchThread());
- service = channel.getRemoteService(IFileSystem.class);
- rendezvous.arrive();
- }
-
- @Override
- public void onChannelClosed(Throwable error) {
- }
-
- @Override
- public void congestionLevel(int level) {
- }
- });
- // Wait for the end of the opening.
- try {
- rendezvous.waiting(connectTimeout);
- } catch (InterruptedException e) {
- throw new IOException(Messages.TcfInputStream_OpenTCFTimeout);
- }
- if (service != null) {
- rendezvous.reset();
- final FileSystemException[] errors = new FileSystemException[1];
- // Open the file.
- service.open(path, IFileSystem.TCF_O_READ, null, new DoneOpen() {
- @Override
- public void doneOpen(IToken token, FileSystemException error,
- IFileHandle hdl) {
- errors[0] = error;
- handle = hdl;
- // Rendezvous
- rendezvous.arrive();
- }
- });
- try {
- rendezvous.waiting(openTimeout);
- } catch (InterruptedException e) {
- throw new IOException(Messages.TcfInputStream_OpenFileTimeout);
- }
- if (errors[0] != null) {
- IOException exception = new IOException(errors[0].toString());
- exception.initCause(errors[0]);
- throw exception;
- }
- if (handle == null) {
- throw new IOException(Messages.TcfInputStream_NoFileReturned);
- }
- } else {
- throw new IOException(Messages.TcfInputStream_NoFSServiceAvailable);
- }
- connected = true;
+ void setTimeout(int readTimeout) {
+ this.timeout = readTimeout;
}
/*
@@ -214,9 +94,6 @@ public class TcfInputStream extends InputStream {
*/
@Override
public int read() throws IOException {
- if (!connected) {
- connect();
- }
if (closed)
throw new IOException(Messages.TcfInputStream_StreamClosed);
if (ERROR != null) {
@@ -253,10 +130,10 @@ public class TcfInputStream extends InputStream {
*/
private void readBlock() {
final Rendezvous rendezvous = new Rendezvous();
- service.read(handle, position, chunk_size, new DoneRead() {
+ IFileSystem service = connection.handle.getService();
+ service.read(connection.handle, position, chunk_size, new DoneRead() {
@Override
- public void doneRead(IToken token, FileSystemException error,
- byte[] data, boolean eof) {
+ public void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof) {
if (error != null) {
ERROR = error;
}
@@ -274,7 +151,7 @@ public class TcfInputStream extends InputStream {
});
// Waiting for reading.
try {
- rendezvous.waiting(readTimeout);
+ rendezvous.waiting(timeout);
} catch (InterruptedException e) {
ERROR = new IOException(Messages.TcfInputStream_ReadTimeout);
}
@@ -287,21 +164,8 @@ public class TcfInputStream extends InputStream {
*/
@Override
public void close() throws IOException {
- if (connected && !closed) {
- final Rendezvous rendezvous = new Rendezvous();
- service.close(handle, new DoneClose() {
- @Override
- public void doneClose(IToken token, FileSystemException error) {
- rendezvous.arrive();
- }
- });
- try {
- rendezvous.waiting(closeTimeout);
- } catch (InterruptedException e) {
- throw new IOException(Messages.TcfInputStream_CloseTimeout);
- }
- channel.close();
- super.close();
+ if (!closed) {
+ connection.closeStream(this);
closed = true;
}
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java
new file mode 100644
index 000000000..b0175744e
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfOutputStream.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.internal.url;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneWrite;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.te.tcf.filesystem.internal.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 chunk size of the writing buffer.
+ int chunk_size = 0;
+
+ // The URL Connection
+ TcfURLConnection connection;
+
+ // The timeout for writing data.
+ int timeout;
+ /**
+ * 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;
+ this.chunk_size = chunk_size;
+ buffer = new byte[chunk_size];
+ offset = 0;
+ }
+
+ /**
+ * Set the timeout for writing a file.
+ *
+ * @param timeout The timeout for writing a file.
+ */
+ void setTimeout(int timeout){
+ this.timeout = timeout;
+ }
+
+ /* (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) {
+ final Rendezvous rendezvous = new Rendezvous();
+ IFileSystem service = connection.handle.getService();
+ 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;
+ rendezvous.arrive();
+ }
+ });
+ // Waiting for writing.
+ try {
+ rendezvous.waiting(timeout);
+ } catch (InterruptedException e) {
+ ERROR = new IOException(Messages.TcfOutputStream_WriteTimeout);
+ }
+ }
+ }
+
+ /* (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.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java
index 7cf06beda..7b24e513f 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/internal/url/TcfURLConnection.java
@@ -9,16 +9,29 @@
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.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 org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
-import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneClose;
+import org.eclipse.tm.tcf.services.IFileSystem.DoneOpen;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFChannelException;
+import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages;
+import org.eclipse.tm.te.tcf.filesystem.model.FSModel;
+import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode;
+
/**
- * The URL connection returned by TCF url stream service used to handle
- * "tcf" stream protocol.
+ * The URL connection returned by TCF stream service used to handler "tcf"
+ * stream protocol.
*/
public class TcfURLConnection extends URLConnection {
// Default connecting timeout.
@@ -27,29 +40,38 @@ public class TcfURLConnection extends URLConnection {
private static final int DEFAULT_OPEN_TIMEOUT = 5000;
// Default file reading timeout.
private static final int DEFAULT_READ_TIMEOUT = 5000;
- //Default file closing timeout.
+ // 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.
private String path;
// The timeout for opening a file.
- private long openTimeout;
+ private int openTimeout;
// The timeout for closing a file.
- private long closeTimeout;
+ private int closeTimeout;
+
+ // The TCF channel used to open and read the resource.
+ IChannel channel;
+ // The file's handle
+ IFileHandle handle;
+
/**
- * 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.
+ * 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.
*
- * @param url The URL of the resource.
+ * @param url
+ * The URL of the resource.
*/
public TcfURLConnection(URL url) {
super(url);
@@ -57,16 +79,14 @@ public class TcfURLConnection extends URLConnection {
// /<TCF_AGENT_ID>/remote/path/to/the/resource...
path = url.getPath();
int slash = path.indexOf("/", 1); //$NON-NLS-1$
- String peerId;
if (slash != -1){
- peerId = path.substring(1, slash);
path = path.substring(slash);
if (path.matches("/[A-Za-z]:.*")) path = path.substring(1); //$NON-NLS-1$
- }else{
- peerId = path.substring(1);
}
//Get the peer using the peer id.
- peer = Protocol.getLocator().getPeers().get(peerId);
+ FSTreeNode node = FSModel.getInstance().getTreeNode(url);
+ if(node != null)
+ peer = node.peerNode.getPeer();
// Set default timeout.
setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
setOpenTimeout(DEFAULT_OPEN_TIMEOUT);
@@ -86,11 +106,13 @@ public class TcfURLConnection extends URLConnection {
/**
* Set the timeout for closing a file.
*
- * @param closeTimeout the timeout in milliseconds.
+ * @param closeTimeout
+ * the timeout in milliseconds.
*/
- public void setCloseTimeout(long closeTimeout) {
+ public void setCloseTimeout(int closeTimeout) {
this.closeTimeout = closeTimeout;
}
+
/**
* Get the timeout for opening a file.
*
@@ -103,29 +125,90 @@ public class TcfURLConnection extends URLConnection {
/**
* Set the timeout for opening a file.
*
- * @param openTimeout the timeout in milliseconds.
+ * @param openTimeout
+ * the timeout in milliseconds.
*/
- public void setOpenTimeout(long openTimeout) {
+ 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 = TCFUtilities.openChannel(peer);
+ } catch (TCFChannelException e) {
+ throw new IOException(e.getLocalizedMessage());
+ }
+ if (channel != null) {
+ IFileSystem service = channel.getRemoteService(IFileSystem.class);
+ if (service != null) {
+ final Rendezvous rendezvous = new Rendezvous();
+ 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;
+ service.open(path, open_flag, null, new DoneOpen() {
+ @Override
+ public void doneOpen(IToken token, FileSystemException error, IFileHandle hdl) {
+ errors[0] = error;
+ handle = hdl;
+ // Rendezvous
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(openTimeout);
+ } catch (InterruptedException e) {
+ throw new IOException(Messages.TcfURLConnection_OpenFileTimeout);
+ }
+ 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.TcfURLConnection_NoFSServiceAvailable);
+ }
+ }
+ }
+
/*
* (non-Javadoc)
+ *
* @see java.net.URLConnection#connect()
*/
@Override
public void connect() throws IOException {
if (!connected) {
- inputStream = new TcfInputStream(peer, path);
- inputStream.setConnectTimeout(getConnectTimeout());
- inputStream.setOpenTimeout(getOpenTimeout());
- inputStream.setReadTimeout(getReadTimeout());
- inputStream.setCloseTimeout(getCloseTimeout());
+ openFile();
+ if (doInput) {
+ inputStream = new TcfInputStream(this);
+ inputStream.setTimeout(getReadTimeout());
+ }
+ if (doOutput) {
+ outputStream = new TcfOutputStream(this);
+ outputStream.setTimeout(getReadTimeout());
+ }
connected = true;
}
}
/*
* (non-Javadoc)
+ *
* @see java.net.URLConnection#getInputStream()
*/
@Override
@@ -134,4 +217,72 @@ public class TcfURLConnection extends URLConnection {
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) {
+ final Rendezvous rendezvous = new Rendezvous();
+ IFileSystem service = handle.getService();
+ service.close(handle, new DoneClose() {
+ @Override
+ public void doneClose(IToken token, FileSystemException error) {
+ rendezvous.arrive();
+ }
+ });
+ try {
+ rendezvous.waiting(closeTimeout);
+ } catch (InterruptedException e) {
+ throw new IOException(Messages.TcfURLConnection_CloseFileTimeout);
+ }
+ channel.close();
+ }
+ }
+
+ /**
+ * 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.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java
new file mode 100644
index 000000000..a452855e4
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/CacheState.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tm.te.tcf.filesystem.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.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java
index a92f26c88..b0c977f26 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSModel.java
@@ -9,11 +9,18 @@
*******************************************************************************/
package org.eclipse.tm.te.tcf.filesystem.model;
+import java.io.File;
+import java.net.URL;
+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.Assert;
import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.tm.te.tcf.filesystem.internal.events.INodeStateListener;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.CacheManager;
/**
* The file system model implementation.
@@ -22,13 +29,37 @@ import org.eclipse.core.runtime.PlatformObject;
* model root node per peer id.
*/
public final class FSModel extends PlatformObject {
+ // Shared instance
+ private static final FSModel instance = new FSModel();
+ /**
+ * Get the shared instance of File System model.
+ * @return The File System Model.
+ */
+ public static FSModel getInstance(){
+ return instance;
+ }
/**
* The file system model root node cache. The keys
* are the peer id's.
*/
- private final Map<String, FSTreeNode> roots = new HashMap<String, FSTreeNode>();
+ private final Map<String, FSTreeNode> roots;
+ // The table mapping the local file to the fileNodes.
+ private Map<String, FSTreeNode> fileNodes;
+ // The table mapping the URL to the fileNodes.
+ private Map<URL, FSTreeNode> urlNodes;
+ // Node state listeners.
+ private List<INodeStateListener> listeners;
/**
+ * Create a File System Model.
+ */
+ private FSModel() {
+ roots = Collections.synchronizedMap(new HashMap<String, FSTreeNode>());
+ fileNodes = Collections.synchronizedMap(new HashMap<String, FSTreeNode>());
+ urlNodes = Collections.synchronizedMap(new HashMap<URL, FSTreeNode>());
+ listeners = Collections.synchronizedList(new ArrayList<INodeStateListener>());
+ }
+ /**
* Returns the file system model root node for the peer identified
* by the given peer id.
*
@@ -59,5 +90,74 @@ public final class FSModel extends PlatformObject {
*/
public void dispose() {
roots.clear();
+ fileNodes.clear();
+ urlNodes.clear();
+ }
+
+ /**
+ * Called to add an FSTreeNode to the two maps, i.e., the location-node map and
+ * the cache-location map.
+ * @param node The FSTreeNode to be added.
+ */
+ public void addNode(FSTreeNode node){
+ File cacheFile = CacheManager.getInstance().getCacheFile(node);
+ fileNodes.put(cacheFile.getAbsolutePath(), node);
+ urlNodes.put(node.getLocationURL(), node);
+ }
+
+ /**
+ * Get the FSTreeNode given its local file's path.
+ *
+ * @param path The local file's path.
+ * @return The FSTreeNode
+ */
+ public FSTreeNode getTreeNode(String path) {
+ return fileNodes.get(path);
+ }
+
+ /**
+ * Get the FSTreeNode given its URL location
+ * @param location the FSTreeNode's location
+ * @return the FSTreeNode
+ */
+ public FSTreeNode getTreeNode(URL location){
+ return urlNodes.get(location);
+ }
+
+ /**
+ * Add an INodeStateListener to the File System model if it is not
+ * in the listener list yet.
+ *
+ * @param listener The INodeStateListener to be added.
+ */
+ public void addNodeStateListener(INodeStateListener listener){
+ if(!listeners.contains(listener)){
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove the INodeStateListener from the File System model if it
+ * exists in the listener list.
+ *
+ * @param listener The INodeStateListener to be removed.
+ */
+ public void removeNodeStateListener(INodeStateListener listener){
+ if(listeners.contains(listener)){
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Fire a node state changed event with the specified node.
+ *
+ * @param node The node whose state has changed.
+ */
+ public void fireNodeStateChanged(FSTreeNode node){
+ synchronized(listeners){
+ for(INodeStateListener listener:listeners){
+ listener.stateChanged(node);
+ }
+ }
}
}
diff --git a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java
index 203a87963..cbb2f9a1d 100644
--- a/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java
+++ b/target_explorer/plugins/org.eclipse.tm.te.tcf.filesystem/src/org/eclipse/tm/te/tcf/filesystem/model/FSTreeNode.java
@@ -15,33 +15,27 @@ package org.eclipse.tm.te.tcf.filesystem.model;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.PlatformObject;
-import org.eclipse.tm.tcf.protocol.IChannel;
-import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener;
-import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IFileSystem;
-import org.eclipse.tm.tcf.services.IFileSystem.DoneUser;
-import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
import org.eclipse.tm.te.tcf.filesystem.interfaces.IWindowsFileAttributes;
import org.eclipse.tm.te.tcf.filesystem.internal.UserAccount;
-import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous;
+import org.eclipse.tm.te.tcf.filesystem.internal.handlers.UserManager;
import org.eclipse.tm.te.tcf.filesystem.internal.url.TcfURLConnection;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel;
/**
* Target Explorer: 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.
+ * <b>Note:</b> Node construction and child list access is limited to the TCF
+ * event dispatch thread.
*/
public final class FSTreeNode extends PlatformObject {
- // The key to save and retrieve the user account in a peer model.
- private static final String USER_ACCOUNT_KEY = "user.account"; //$NON-NLS-1$
private final UUID uniqueId = UUID.randomUUID();
@@ -73,7 +67,7 @@ public final class FSTreeNode extends PlatformObject {
/**
* The tree node children.
*/
- private List<FSTreeNode> children = new ArrayList<FSTreeNode>();
+ private List<FSTreeNode> children;
/**
* Flag to mark once the children of the node got queried
@@ -90,10 +84,13 @@ public final class FSTreeNode extends PlatformObject {
*/
public FSTreeNode() {
super();
+ children = Collections.synchronizedList(new ArrayList<FSTreeNode>());
Assert.isTrue(Protocol.isDispatchThread());
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.lang.Object#hashCode()
*/
@Override
@@ -104,8 +101,9 @@ public final class FSTreeNode extends PlatformObject {
/**
* Returns the children list storage object.
* <p>
- * <b>Note:</b> This method must be called from within the TCF event dispatch thread only!
- *
+ * <b>Note:</b> This method must be called from within the TCF event
+ * dispatch thread only!
+ *
* @return The children list storage object.
*/
public final List<FSTreeNode> getChildren() {
@@ -113,18 +111,24 @@ public final class FSTreeNode extends PlatformObject {
return children;
}
- /* (non-Javadoc)
+ /*
+ * (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 FSTreeNode) {
- return uniqueId.equals(((FSTreeNode)obj).uniqueId);
+ return uniqueId.equals(((FSTreeNode) obj).uniqueId);
}
return super.equals(obj);
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.lang.Object#toString()
*/
@Override
@@ -137,6 +141,7 @@ public final class FSTreeNode extends PlatformObject {
/**
* Return if the node is a Windows file/folder node.
+ *
* @return true if it is a Windows node, or else false.
*/
public boolean isWindowsNode() {
@@ -145,6 +150,7 @@ public final class FSTreeNode extends PlatformObject {
/**
* Return if the node is a file.
+ *
* @return true if it is a file, or else false.
*/
public boolean isFile() {
@@ -153,6 +159,7 @@ public final class FSTreeNode extends PlatformObject {
/**
* Return if the node is a directory.
+ *
* @return true if it is a directory, or else false.
*/
public boolean isDirectory() {
@@ -161,7 +168,9 @@ public final class FSTreeNode extends PlatformObject {
/**
* Return if the attribute specified by the mask bit is turned on.
- * @param bit The attribute's mask bit.
+ *
+ * @param bit
+ * The attribute's mask bit.
* @return true if it is on, or else false.
*/
public boolean isWin32AttrOn(int bit) {
@@ -174,6 +183,7 @@ public final class FSTreeNode extends PlatformObject {
/**
* Return if this file/folder is hidden.
+ *
* @return true if it is hidden, or else false.
*/
public boolean isHidden() {
@@ -182,6 +192,7 @@ public final class FSTreeNode extends PlatformObject {
/**
* Return if this file/folder is read-only.
+ *
* @return true if it is read-only, or else false.
*/
public boolean isReadOnly() {
@@ -191,8 +202,9 @@ public final class FSTreeNode extends PlatformObject {
/**
* Get the location of a file/folder node using the format of the file
* system's platform.
- *
- * @param node The file/folder node.
+ *
+ * @param node
+ * The file/folder node.
* @return The location of the file/folder.
*/
public String getLocation() {
@@ -207,11 +219,12 @@ public final class FSTreeNode extends PlatformObject {
/**
* Get the location of a file/folder.
- *
- * @param cross If the format is cross-platform.
+ *
+ * @param cross
+ * If the format is cross-platform.
* @return The path to the file/folder.
*/
- private String getLocation(boolean cross) {
+ public String getLocation(boolean cross) {
if (parent == null)
return null;
String pLoc = parent.getLocation(cross);
@@ -223,23 +236,28 @@ public final class FSTreeNode extends PlatformObject {
}
/**
- * 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)}
- *
+ * 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)}
+ *
* @return The URL of the file/folder.
* @throws MalformedURLException
*/
- public URL getLocationURL() throws MalformedURLException {
- String id = peerNode.getPeer().getID();
- String path = getLocation(true);
- String url = TcfURLConnection.PROTOCOL_SCHEMA+":///" + id + (isWindowsNode() ? "/" + path : path); //$NON-NLS-1$ //$NON-NLS-2$
- return new URL(url);
+ public URL getLocationURL() {
+ try {
+ String id = peerNode.getPeer().getID();
+ String path = getLocation(true);
+ String location = TcfURLConnection.PROTOCOL_SCHEMA + ":/" + id + (isWindowsNode() ? "/" + path : path); //$NON-NLS-1$ //$NON-NLS-2$
+ return new URL(location);
+ } catch (MalformedURLException e) {
+ assert false;
+ return null;
+ }
}
/**
* If this node is a root node.
+ *
* @return true if this node is a root node.
*/
public boolean isRoot() {
@@ -248,11 +266,11 @@ public final class FSTreeNode extends PlatformObject {
/**
* If this file is readable.
- *
+ *
* @return true if it is readable.
*/
public boolean isReadable() {
- UserAccount account = getUserAccount();
+ UserAccount account = UserManager.getInstance().getUserAccount(peerNode);
if (account != null && attr != null) {
if (attr.uid == account.getEUID()) {
return (attr.permissions & IFileSystem.S_IRUSR) != 0;
@@ -267,11 +285,11 @@ public final class FSTreeNode extends PlatformObject {
/**
* If this file is writable.
- *
+ *
* @return true if it is writable.
*/
public boolean isWritable() {
- UserAccount account = getUserAccount();
+ UserAccount account = UserManager.getInstance().getUserAccount(peerNode);
if (account != null && attr != null) {
if (attr.uid == account.getEUID()) {
return (attr.permissions & IFileSystem.S_IWUSR) != 0;
@@ -286,11 +304,11 @@ public final class FSTreeNode extends PlatformObject {
/**
* If this file is executable.
- *
+ *
* @return true if it is executable.
*/
public boolean isExecutable() {
- UserAccount account = getUserAccount();
+ UserAccount account = UserManager.getInstance().getUserAccount(peerNode);
if (account != null && attr != null) {
if (attr.uid == account.getEUID()) {
return (attr.permissions & IFileSystem.S_IXUSR) != 0;
@@ -302,108 +320,4 @@ public final class FSTreeNode extends PlatformObject {
}
return false;
}
-
- /**
- * Get the information of the client user account.
- *
- * @return The client user account's information.
- */
- private UserAccount getUserAccount() {
- UserAccount account = getUserAccount(peerNode);
- if (account == null) {
- final Rendezvous rendezvous = new Rendezvous();
- IChannel channel = peerNode.getPeer().openChannel();
- channel.addChannelListener(new IChannelListener() {
- @Override
- public void onChannelOpened() {
- rendezvous.arrive();
- }
-
- @Override
- public void onChannelClosed(Throwable error) {
- }
-
- @Override
- public void congestionLevel(int level) {
- }
- });
- try {
- rendezvous.waiting(5000L);
- } catch (InterruptedException e) {
- return null;
- }
- rendezvous.reset();
- IFileSystem service = channel.getRemoteService(IFileSystem.class);
- 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);
- }
- rendezvous.arrive();
- }
- });
- try {
- rendezvous.waiting(5000L);
- } catch (InterruptedException e) {
- return null;
- }
- if (accounts[0] == null)
- return null;
- account = accounts[0];
- setUserAccount(peerNode, account);
- }
- return account;
- }
-
- /**
- * 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 getUserAccount(final IPeerModel peer) {
- UserAccount account;
- if (Protocol.isDispatchThread()) {
- account = (UserAccount) peer.getProperty(USER_ACCOUNT_KEY);
- } else {
- final UserAccount[] accounts = new UserAccount[1];
- Protocol.invokeAndWait(new Runnable() {
- @Override
- public void run() {
- accounts[0] = (UserAccount) peer
- .getProperty(USER_ACCOUNT_KEY);
- }
- });
- account = accounts[0];
- }
- return account;
- }
-
- /**
- * 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.
- */
- private void setUserAccount(final IPeerModel peer, final UserAccount account) {
- assert peer != null && account != null;
- if (Protocol.isDispatchThread()) {
- peer.setProperty(USER_ACCOUNT_KEY, account);
- } else {
- Protocol.invokeAndWait(new Runnable() {
- @Override
- public void run() {
- peer.setProperty(USER_ACCOUNT_KEY, account);
- }
- });
- }
- }
}

Back to the top