OSDN Git Service

New command: LinkCommand (for create symlinks)
authorjruesga <jorge@ruesga.com>
Thu, 11 Oct 2012 23:27:12 +0000 (01:27 +0200)
committerjruesga <jorge@ruesga.com>
Thu, 11 Oct 2012 23:27:12 +0000 (01:27 +0200)
res/xml/command_list.xml
src/com/cyanogenmod/explorer/commands/ExecutableCreator.java
src/com/cyanogenmod/explorer/commands/LinkExecutable.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/commands/shell/LinkCommand.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java
src/com/cyanogenmod/explorer/util/CommandHelper.java
src/com/cyanogenmod/explorer/util/StorageHelper.java
tests/src/com/cyanogenmod/explorer/commands/shell/LinkCommandTest.java [new file with mode: 0644]

index 9246c65..8161115 100644 (file)
@@ -49,6 +49,7 @@
   <command commandId="folderusage" commandPath="/system/bin/ls" commandArgs="-alR %1$s" />
   <command commandId="groups" commandPath="/system/xbin/groups" commandArgs="" />
   <command commandId="id" commandPath="/system/bin/id" commandArgs="-Gn" />
+  <command commandId="link" commandPath="/system/bin/ln" commandArgs="-s %1$s %2$s" />
   <command commandId="ls" commandPath="cd" commandArgs="%1$s &amp;&amp; /system/bin/ls -al %1$s | /system/xbin/grep -v -e '^l' &amp;&amp; echo '>SIMLINKS>' &amp;&amp; /system/bin/ls -al %1$s | { /system/xbin/grep -e '^l' || true; } &amp;&amp; echo '>SIMLINKS_DATA>' &amp;&amp; /system/bin/ls -aF %1$s | /system/xbin/grep -e '^l' | /system/xbin/cut -d ' ' -f2- &amp;&amp; /system/bin/ls -aF %1$s | /system/xbin/grep -e '^l' | /system/xbin/cut -d ' ' -f2- | awk '{print &quot;\\&quot;&quot;$0&quot;\\&quot;&quot;}' | /system/xbin/xargs -r -n1 /system/xbin/readlink -f &amp;&amp; /system/bin/ls -F %1$s | /system/xbin/grep -e '^l' | /system/xbin/cut -d ' ' -f2- | awk '{print &quot;\\&quot;&quot;$0&quot;\\&quot;&quot;}' | /system/xbin/xargs -r -n1 /system/xbin/readlink -f | awk '{print &quot;\\&quot;&quot;$0&quot;\\&quot;&quot;}' | { /system/xbin/xargs -r /system/bin/ls -ald || echo; }" />
   <command commandId="mkdir" commandPath="/system/bin/mkdir" commandArgs="%1$s" />
   <command commandId="mount" commandPath="/system/bin/mount" commandArgs="-o %1$s,remount -t auto %2$s %3$s" />
index 2f6703f..5a4f2f3 100644 (file)
@@ -201,6 +201,17 @@ public interface ExecutableCreator {
     IdentityExecutable createIdentityExecutable() throws CommandNotFoundException;
 
     /**
+     * Method that creates a symlink of an other file system object.
+     *
+     * @param src The absolute path to the source fso
+     * @param link The absolute path to the link fso
+     * @return LinkExecutable A {@link LinkExecutable} executable implementation reference
+     * @throws CommandNotFoundException If the executable can't be created
+     */
+    LinkExecutable createLinkExecutable(
+            String src, String link) throws CommandNotFoundException;
+
+    /**
      * Method that creates an executable for list files of a directory.
      *
      * @param src The directory where to do the listing
diff --git a/src/com/cyanogenmod/explorer/commands/LinkExecutable.java b/src/com/cyanogenmod/explorer/commands/LinkExecutable.java
new file mode 100644 (file)
index 0000000..1064dfa
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.explorer.commands;
+
+/**
+ * An interface that represents an executable for create symlinks to other file system objects.
+ */
+public interface LinkExecutable extends WritableExecutable {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Boolean getResult();
+}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/LinkCommand.java b/src/com/cyanogenmod/explorer/commands/shell/LinkCommand.java
new file mode 100644 (file)
index 0000000..09b29a0
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.explorer.commands.shell;
+
+import com.cyanogenmod.explorer.commands.LinkExecutable;
+import com.cyanogenmod.explorer.console.CommandNotFoundException;
+import com.cyanogenmod.explorer.console.ExecutionException;
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+import com.cyanogenmod.explorer.model.MountPoint;
+import com.cyanogenmod.explorer.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for create a symlink of an other file system object.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?ln"}
+ */
+public class LinkCommand extends SyncResultProgram implements LinkExecutable {
+
+    private static final String ID = "link";  //$NON-NLS-1$
+    private Boolean mRet;
+    private final String mLink;
+
+    /**
+     * Constructor of <code>LinkCommand</code>.
+     *
+     * @param src The path of the source file
+     * @param link The path of the link file
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     */
+    public LinkCommand(String src, String link) throws InvalidCommandDefinitionException {
+        super(ID, src, link);
+        this.mLink = link;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void parse(String in, String err) throws ParseException {
+        //Release the return object
+        this.mRet = Boolean.TRUE;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Boolean getResult() {
+        return this.mRet;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void checkExitCode(int exitCode)
+            throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+        // Not raise insufficient permissions if the link is in  
+        if (exitCode != 0) {
+            throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public MountPoint getWritableMountPoint() {
+        return MountPointHelper.getMountPointFromDirectory(this.mLink);
+    }
+}
index 3d455ec..8e308e8 100644 (file)
@@ -33,6 +33,7 @@ import com.cyanogenmod.explorer.commands.FindExecutable;
 import com.cyanogenmod.explorer.commands.FolderUsageExecutable;
 import com.cyanogenmod.explorer.commands.GroupsExecutable;
 import com.cyanogenmod.explorer.commands.IdentityExecutable;
+import com.cyanogenmod.explorer.commands.LinkExecutable;
 import com.cyanogenmod.explorer.commands.ListExecutable;
 import com.cyanogenmod.explorer.commands.MountExecutable;
 import com.cyanogenmod.explorer.commands.MountPointInfoExecutable;
@@ -275,6 +276,20 @@ public class ShellExecutableCreator implements ExecutableCreator {
      * {@inheritDoc}
      */
     @Override
+    public LinkExecutable createLinkExecutable(String src, String link)
+            throws CommandNotFoundException {
+        try {
+            return new LinkCommand(src, link);
+        } catch (InvalidCommandDefinitionException icdEx) {
+            throw new CommandNotFoundException("LinkCommand", icdEx); //$NON-NLS-1$
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public ListExecutable createListExecutable(String src)
             throws CommandNotFoundException {
         try {
index f79eb19..f9793b8 100644 (file)
@@ -36,6 +36,7 @@ import com.cyanogenmod.explorer.commands.FindExecutable;
 import com.cyanogenmod.explorer.commands.FolderUsageExecutable;
 import com.cyanogenmod.explorer.commands.GroupsExecutable;
 import com.cyanogenmod.explorer.commands.IdentityExecutable;
+import com.cyanogenmod.explorer.commands.LinkExecutable;
 import com.cyanogenmod.explorer.commands.ListExecutable;
 import com.cyanogenmod.explorer.commands.MountExecutable;
 import com.cyanogenmod.explorer.commands.MountPointInfoExecutable;
@@ -535,6 +536,39 @@ public final class CommandHelper {
        return executable.getResult();
    }
 
+   /**
+    * Method that creates a symlink of an other file system object.
+    *
+    * @param context The current context (needed if console == null)
+    * @param src The absolute path to the source fso
+     * @param link The absolute path to the link fso
+    * @param console The console in which execute the program. <code>null</code>
+    * to attach to the default console
+    * @return boolean The operation result
+    * @throws FileNotFoundException If the initial directory not exists
+    * @throws IOException If initial directory can't not be checked
+    * @throws InvalidCommandDefinitionException If the command has an invalid definition
+    * @throws NoSuchFileOrDirectory If the file or directory was not found
+    * @throws ConsoleAllocException If the console can't be allocated
+    * @throws InsufficientPermissionsException If an operation requires elevated permissions
+    * @throws CommandNotFoundException If the command was not found
+    * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+    * @throws ExecutionException If the operation returns a invalid exit code
+    * @throws ReadOnlyFilesystemException If the operation writes in a read-only filesystem
+    * @see LinkExecutable
+    */
+   public static boolean createLink(Context context, String src, String link, Console console)
+           throws FileNotFoundException, IOException, ConsoleAllocException,
+           NoSuchFileOrDirectory, InsufficientPermissionsException,
+           CommandNotFoundException, OperationTimeoutException,
+           ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
+       Console c = ensureConsole(context, console);
+       LinkExecutable executable =
+               c.getExecutableFactory().newCreator().createLinkExecutable(src, link);
+       writableExecute(context, executable, c);
+       return executable.getResult().booleanValue();
+   }
+
     /**
      * Method that retrieves the parent directory of a file system object.
      *
index 9a0abff..b15203e 100644 (file)
@@ -20,6 +20,7 @@ import android.os.Environment;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
 
+import com.cyanogenmod.explorer.ExplorerApplication;
 import com.cyanogenmod.explorer.R;
 
 import java.io.File;
@@ -96,4 +97,22 @@ public final class StorageHelper {
         }
     }
 
+    /**
+     * Method that returns if the path is in a volume storage
+     * 
+     * @param path The path
+     * @return boolean If the path is in a volume storage
+     */
+    public static boolean isPathInStorageVolume(String path) {
+        StorageVolume[] volumes =
+                getStorageVolumes(ExplorerApplication.getInstance().getApplicationContext());
+        for (int i=0; i < volumes.length; i++) {
+            StorageVolume vol = volumes[i];
+            if (path.startsWith(vol.getPath())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/LinkCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/LinkCommandTest.java
new file mode 100644 (file)
index 0000000..318475f
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.explorer.commands.shell;
+
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+import com.cyanogenmod.explorer.model.FileSystemObject;
+import com.cyanogenmod.explorer.model.Symlink;
+import com.cyanogenmod.explorer.util.CommandHelper;
+
+/**
+ * A class for testing the {@link LinkCommandTest} command.
+ *
+ * @see DeleteFileCommand
+ */
+public class LinkCommandTest extends AbstractConsoleTest {
+
+    private static final String PATH_SOURCE_OK = "/data/source.txt"; //$NON-NLS-1$
+    private static final String PATH_LINK_OK = "/data/source-link"; //$NON-NLS-1$
+    private static final String PATH_SOURCE_ERROR = "/sdcard/source.txt"; //$NON-NLS-1$
+    private static final String PATH_LINK_ERROR = "/sdcard/source-link"; //$NON-NLS-1$
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRootConsoleNeeded() {
+        return true;
+    }
+
+    /**
+     * Method that performs a test to delete a file.
+     *
+     * @throws Exception If test failed
+     */
+    public void testCreateSymlinkOk() throws Exception {
+        try {
+            CommandHelper.createFile(getContext(), PATH_SOURCE_OK, getConsole());
+            boolean ret = CommandHelper.createLink(
+                    getContext(), PATH_SOURCE_OK, PATH_LINK_OK, getConsole());
+            FileSystemObject fso =
+                    CommandHelper.getFileInfo(getContext(), PATH_LINK_OK, false, getConsole());
+            assertTrue("response==false", ret); //$NON-NLS-1$
+            assertTrue("fso not is Symlink", fso instanceof Symlink); //$NON-NLS-1$
+        } finally {
+            try {
+                CommandHelper.deleteFile(getContext(), PATH_SOURCE_OK, getConsole());
+            } catch (Exception e) {/**NON BLOCK**/}
+            try {
+                CommandHelper.deleteFile(getContext(), PATH_LINK_OK, getConsole());
+            } catch (Exception e) {/**NON BLOCK**/}
+        }
+    }
+
+    /**
+     * Method that performs a test to delete an invalid file.
+     *
+     * @throws Exception If test failed
+     */
+    public void testCreateSymlinkFail() throws Exception {
+        try {
+            CommandHelper.createFile(getContext(), PATH_SOURCE_ERROR, getConsole());
+            boolean ret = CommandHelper.createLink(
+                    getContext(), PATH_SOURCE_ERROR, PATH_LINK_ERROR, getConsole());
+            assertTrue("response==false", ret); //$NON-NLS-1$
+            try {
+                FileSystemObject fso =
+                        CommandHelper.getFileInfo(getContext(), PATH_LINK_ERROR, getConsole());
+                assertTrue("fso != null", fso == null); //$NON-NLS-1$
+            } catch (Exception e) {
+                //OK. getFileInfo throws an exception because the symlink couldn't be created
+            }
+        } catch (InsufficientPermissionsException eex) {
+            // This the expected behaviour because the symlink couldn't be created
+        } finally {
+            try {
+                CommandHelper.deleteFile(getContext(), PATH_SOURCE_ERROR, getConsole());
+            } catch (Exception e) {/**NON BLOCK**/}
+            try {
+                CommandHelper.deleteFile(getContext(), PATH_LINK_ERROR, getConsole());
+            } catch (Exception e) {/**NON BLOCK**/}
+        }
+    }
+
+}