OSDN Git Service

New compress command
authorjruesga <jorge@ruesga.com>
Tue, 16 Oct 2012 23:26:36 +0000 (01:26 +0200)
committerjruesga <jorge@ruesga.com>
Tue, 16 Oct 2012 23:26:36 +0000 (01:26 +0200)
archive: tar
compress: gz, bz2
archive+compress: tgz, tar.gz, tar.bz2

res/xml/command_list.xml
src/com/cyanogenmod/explorer/commands/CompressExecutable.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/commands/ExecutableCreator.java
src/com/cyanogenmod/explorer/commands/shell/Command.java
src/com/cyanogenmod/explorer/commands/shell/CompressCommand.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java
src/com/cyanogenmod/explorer/util/CommandHelper.java
tests/src/com/cyanogenmod/explorer/commands/shell/CompressCommandTest.java [new file with mode: 0644]

index d41f7cf..44411fb 100644 (file)
   <command commandId="bash" commandPath="/system/bin/sh" commandArgs="" />
   <command commandId="su" commandPath="/system/xbin/su" commandArgs="" />
 
-  <!-- Program Comamnds -->
+  <!-- Directory -->
   <command commandId="cd" commandPath="cd" commandArgs="%1$s" />
+  <command commandId="pwd" commandPath="/system/xbin/pwd" commandArgs="-P" />
+
+  <!-- Console info -->
+  <command commandId="groups" commandPath="/system/xbin/groups" commandArgs="" />
+  <command commandId="id" commandPath="/system/bin/id" commandArgs="-Gn" />
+
+  <!-- FileSystem -->
+  <command commandId="mount" commandPath="/system/bin/mount" commandArgs="-o %1$s,remount -t auto %2$s %3$s" />
+  <command commandId="mountpointinfo" commandPath="/system/bin/cat" commandArgs="/proc/mounts" />
+
+  <!-- List/Find/Info -->
+  <command commandId="ls" commandPath="cd" commandArgs="%1$s &amp;&amp; /system/bin/ls -al %1$s | { /system/xbin/grep -v -e '^l' || true; } &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="fileinfo" commandPath="/system/bin/ls" commandArgs="-ald %1$s" />
+  <command commandId="find" commandPath="/system/xbin/find" commandArgs="%1$s \\( -name %2$s -o -name %3$s -o -name %4$s -o -name %5$s -o -name %6$s \\) -exec /system/xbin/echo {} \\; -exec /system/bin/ls -ald {} \\;" />
+  <command commandId="quickfoldersearch" commandPath="/system/bin/ls" commandArgs="-aFd %1$s.* %1$s* | /system/xbin/grep -e '^d' -e '^ld' | /system/xbin/awk '{print $2}'" />
+  <command commandId="readlink" commandPath="cd" commandArgs="%2$s &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/xbin/dirname &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/bin/ls -ald" />
+
+  <!-- Operational -->
   <command commandId="chmod" commandPath="/system/bin/chmod" commandArgs="%1$s %2$s" />
   <command commandId="chown" commandPath="/system/bin/chown" commandArgs="%1$s.%2$s %3$s" />
   <command commandId="cp" commandPath="/system/xbin/cp" commandArgs="-af %1$s %2$s" />
+  <command commandId="link" commandPath="/system/bin/ln" commandArgs="-s %1$s %2$s" />
+  <command commandId="mkdir" commandPath="/system/bin/mkdir" commandArgs="%1$s" />
+  <command commandId="mv" commandPath="/system/bin/mv" commandArgs="%1$s %2$s" />
+  <command commandId="rm" commandPath="/system/bin/rm" commandArgs="%1$s" />
+  <command commandId="rmdir" commandPath="/system/bin/rm" commandArgs="-R %1$s" />
+
+  <!-- Usage -->
   <command commandId="diskusage" commandPath="/system/bin/df" commandArgs="%1$s" />
   <command commandId="diskusageall" commandPath="/system/bin/df" commandArgs="" />
+  <command commandId="folderusage" commandPath="/system/bin/ls" commandArgs="-alR %1$s" />
+
+  <!-- I/O -->
+  <command commandId="read" commandPath="/system/bin/cat" commandArgs="&lt; %1$s" />
+  <command commandId="touch" commandPath="/system/xbin/echo" commandArgs="-n '' >> %1$s" />
+  <command commandId="write" commandPath="/system/bin/dd" commandArgs="bs=4k of=%1$s" />
+
+  <!-- Run -->
+  <command commandId="exec" commandPath="/system/bin/sh" commandArgs="-c %1$s" />
+
+  <!-- Misc -->
   <command commandId="dirname" commandPath="/system/xbin/dirname" commandArgs="%1$s" />
   <command commandId="echo" commandPath="/system/xbin/echo" commandArgs="%1$s" />
-  <command commandId="exec" commandPath="/system/bin/sh" commandArgs="-c %1$s" />
-  <command commandId="fileinfo" commandPath="/system/bin/ls" commandArgs="-ald %1$s" />
-  <command commandId="find" commandPath="/system/xbin/find" commandArgs="%1$s \\( -name %2$s -o -name %3$s -o -name %4$s -o -name %5$s -o -name %6$s \\) -exec /system/xbin/echo {} \\; -exec /system/bin/ls -ald {} \\;" />
-  <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' || true; } &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" />
-  <command commandId="mountpointinfo" commandPath="/system/bin/cat" commandArgs="/proc/mounts" />
-  <command commandId="mv" commandPath="/system/bin/mv" commandArgs="%1$s %2$s" />
+
+  <!-- Process control and info -->
   <command commandId="pid_shell" commandPath="/system/xbin/echo" commandArgs="$$" />
   <command commandId="pid_cmd" commandPath="/system/bin/ps" commandArgs="| /system/xbin/grep %1$s | /system/xbin/grep -w %2$s | /system/xbin/awk '{print $2}'" />
-  <command commandId="pwd" commandPath="/system/xbin/pwd" commandArgs="-P" />
-  <command commandId="quickfoldersearch" commandPath="/system/bin/ls" commandArgs="-aFd %1$s.* %1$s* | /system/xbin/grep -e '^d' -e '^ld' | /system/xbin/awk '{print $2}'" />
-  <command commandId="read" commandPath="/system/bin/cat" commandArgs="&lt; %1$s" />
-  <command commandId="readlink" commandPath="cd" commandArgs="%2$s &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/xbin/dirname &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/bin/ls -ald" />
-  <command commandId="rm" commandPath="/system/bin/rm" commandArgs="%1$s" />
-  <command commandId="rmdir" commandPath="/system/bin/rm" commandArgs="-R %1$s" />
   <command commandId="sendsignal" commandPath="/system/bin/kill" commandArgs="-%1$s %2$s" />
   <command commandId="terminate" commandPath="/system/bin/kill" commandArgs="%1$s" />
-  <command commandId="touch" commandPath="/system/xbin/echo" commandArgs="-n '' >> %1$s" />
-  <command commandId="write" commandPath="/system/bin/dd" commandArgs="bs=4k of=%1$s" />
+
+  <!-- Compress commands -->
+  <command commandId="compress" commandPath="/system/xbin/tar" commandArgs="%1$scvf %2$s [@]" />
+  <command commandId="gzip" commandPath="/system/bin/gzip" commandArgs="%1$s" />
+  <command commandId="bzip" commandPath="/system/xbin/bzip2" commandArgs="-f %1$s" />
 
 </CommandList>
\ No newline at end of file
diff --git a/src/com/cyanogenmod/explorer/commands/CompressExecutable.java b/src/com/cyanogenmod/explorer/commands/CompressExecutable.java
new file mode 100644 (file)
index 0000000..0ba0ba9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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 compress file system objects.
+ */
+public interface CompressExecutable extends AsyncResultExecutable {
+
+    /**
+     * Method that returns the result of the operation
+     *
+     * @return Boolean The result of the operation
+     */
+    Boolean getResult();
+
+    /**
+     * Method that returns the name out compressed file
+     *
+     * @return Method that returns the name of the compressed file
+     */
+    String getOutCompressedFile();
+}
index ec1d8e4..59b38b4 100644 (file)
@@ -17,6 +17,7 @@
 package com.cyanogenmod.explorer.commands;
 
 import com.cyanogenmod.explorer.commands.ListExecutable.LIST_MODE;
+import com.cyanogenmod.explorer.commands.shell.CompressCommand.CompressionMode;
 import com.cyanogenmod.explorer.console.CommandNotFoundException;
 import com.cyanogenmod.explorer.model.Group;
 import com.cyanogenmod.explorer.model.MountPoint;
@@ -379,4 +380,32 @@ public interface ExecutableCreator {
             String file, AsyncResultListener asyncResultListener)
             throws CommandNotFoundException;
 
+    /**
+     * Method that creates an executable for archive-compress file system objects.
+     *
+     * @param mode The compression mode
+     * @param dst The destination compressed file
+     * @param src The array of source files to compress
+     * @param asyncResultListener The listener where to return partial results
+     * @return CompressExecutable A {@link CompressExecutable} executable implementation reference
+     * @throws CommandNotFoundException If the executable can't be created
+     */
+    CompressExecutable createCompressExecutable(
+            CompressionMode mode, String dst, String[] src,
+            AsyncResultListener asyncResultListener)
+            throws CommandNotFoundException;
+
+    /**
+     * Method that creates an executable for compress a file system object.
+     *
+     * @param mode The compression mode
+     * @param src The file to compress
+     * @param asyncResultListener The listener where to return partial results
+     * @return CompressExecutable A {@link CompressExecutable} executable implementation reference
+     * @throws CommandNotFoundException If the executable can't be created
+     */
+    CompressExecutable createCompressExecutable(
+            CompressionMode mode, String src, AsyncResultListener asyncResultListener)
+            throws CommandNotFoundException;
+
 }
index 7027002..3c1e5da 100644 (file)
@@ -47,10 +47,12 @@ public abstract class Command {
     private static final String TAG_STARTCODE = "startcode"; //$NON-NLS-1$
     private static final String TAG_EXITCODE = "exitcode"; //$NON-NLS-1$
 
+    private static final String EXPANDED_ARGS = "[@]"; //$NON-NLS-1$
+
     private final String mId;
     private String mCmd;
     private String mArgs;   // The real arguments
-    private final Object[] mCmdArgs;  //This arguments to be formatted
+    private final Object[] mCmdArgs;  //The arguments to be formatted
 
     private static String sStartCodeCmd;
     private static String sExitCodeCmd;
@@ -105,6 +107,40 @@ public abstract class Command {
     }
 
     /**
+     * Method that add expended arguments to the arguments. This is defined with a
+     * <code>[@]</code> expression in the <code>commandArgs</code> attribute of the
+     * command xml definition file.
+     *
+     * @param args The expanded arguments
+     * @param prepare Indicates if the argument must be prepared
+     */
+    protected void addExpandedArguments(String[] args, boolean prepare) {
+        // Don't use of regexp to avoid the need to parse of args to make it compilable.
+        // Only one expanded argument of well known characters
+        int pos = this.mArgs.indexOf(EXPANDED_ARGS);
+        if (pos != -1) {
+            int cc = args.length;
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < cc; i++) {
+                //Quote the arguments?
+                if (prepare) {
+                    sb = sb.append("\"" + //$NON-NLS-1$
+                            ShellHelper.prepareArgument(args[i]) + "\""); //$NON-NLS-1$
+                    sb = sb.append(" "); //$NON-NLS-1$
+                } else {
+                    sb = sb.append(ShellHelper.prepareArgument(args[i]));
+                    sb = sb.append(" "); //$NON-NLS-1$
+                }
+            }
+
+            // Replace the expanded argument
+            String start = this.mArgs.substring(0, pos);
+            String end = this.mArgs.substring(pos+EXPANDED_ARGS.length());
+            this.mArgs = start + sb.toString() + end;
+        }
+    }
+
+    /**
      * Method that return id the command had to trace his operations
      *
      * @return boolean If the command had to trace
diff --git a/src/com/cyanogenmod/explorer/commands/shell/CompressCommand.java b/src/com/cyanogenmod/explorer/commands/shell/CompressCommand.java
new file mode 100644 (file)
index 0000000..5397880
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.CompressExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
+import com.cyanogenmod.explorer.console.CommandNotFoundException;
+import com.cyanogenmod.explorer.console.ExecutionException;
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+
+/**
+ * A class for compress file system objects
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?tar"}
+ */
+public class CompressCommand extends AsyncResultProgram implements CompressExecutable {
+
+    /**
+     * An enumeration of implemented compression modes.
+     */
+    public enum CompressionMode {
+        /**
+         * Without compression
+         */
+        NONE("", "", ""), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        /**
+         * Compress using Gzip algorithm
+         */
+        GZIP(GZIP_ID, "z", "gz"), //$NON-NLS-1$ //$NON-NLS-2$
+        /**
+         * Compress using Bzip algorithm
+         */
+        BZIP(BZIP_ID, "j", "bz2"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        String mId;
+        String mFlag;
+        String mExtension;
+
+        /**
+         * Constructor of <code>CompressionMode</code>
+         *
+         * @param id The command identifier
+         * @param flag The tar compression flag
+         */
+        private CompressionMode(String id, String flag, String extension) {
+            this.mId = id;
+            this.mFlag = flag;
+            this.mExtension = extension;
+        }
+    }
+
+    private static final String TAR_ID = "compress"; //$NON-NLS-1$
+    private static final String GZIP_ID = "gzip"; //$NON-NLS-1$
+    private static final String BZIP_ID = "bzip"; //$NON-NLS-1$
+
+    private Boolean mResult;
+    private String mPartial;
+
+    private final String mOutFile;
+
+    /**
+     * Constructor of <code>CompressCommand</code>. This method creates an archive-compressed
+     * file from one or various file system objects.
+     *
+     * @param mode The compression mode
+     * @param dst The absolute path of the new compress file
+     * @param src An array of file system objects to compress
+     * @param asyncResultListener The partial result listener
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     */
+    public CompressCommand(
+            CompressionMode mode, String dst, String[] src, AsyncResultListener asyncResultListener)
+            throws InvalidCommandDefinitionException {
+        super(TAR_ID, asyncResultListener, new String[]{mode.mFlag, dst});
+        addExpandedArguments(src, true);
+
+        // Create the output file
+        this.mOutFile = dst;
+    }
+
+    /**
+     * Constructor of <code>CompressCommand</code>. This method creates a compressed
+     * file from one file.
+     *
+     * @param mode The compression mode
+     * @param src The file to compress
+     * @param asyncResultListener The partial result listener
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     */
+    public CompressCommand(
+            CompressionMode mode, String src, AsyncResultListener asyncResultListener)
+            throws InvalidCommandDefinitionException {
+        super(mode.mId, asyncResultListener, resolveArguments(mode, src));
+
+        // Create the output file
+        this.mOutFile = resolveOutputFile(mode, src);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onStartParsePartialResult() {
+        this.mResult = Boolean.FALSE;
+        this.mPartial = ""; //$NON-NLS-1$
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onEndParsePartialResult(boolean canceled) {
+        // Send the las partial data
+        if (this.mPartial != null && this.mPartial.length() > 0) {
+            if (getAsyncResultListener() != null) {
+                getAsyncResultListener().onPartialResult(this.mPartial);
+            }
+        }
+        this.mPartial = ""; //$NON-NLS-1$
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onParsePartialResult(final String partialIn) {
+        if (partialIn == null || partialIn.length() ==0) return;
+        boolean endsWithNewLine = partialIn.endsWith("\n"); //$NON-NLS-1$
+        String[] lines = partialIn.split("\n"); //$NON-NLS-1$
+
+        // Append the pending data to the first line
+        lines[0] = this.mPartial + lines[0];
+
+        // Return all the lines, except the last
+        for (int i=0; i<lines.length-1; i++) {
+            if (getAsyncResultListener() != null) {
+                getAsyncResultListener().onPartialResult(lines[i]);
+            }
+        }
+
+        // Return the last line?
+        if (endsWithNewLine) {
+            if (getAsyncResultListener() != null) {
+                getAsyncResultListener().onPartialResult(lines[lines.length-1]);
+            }
+            this.mPartial = ""; //$NON-NLS-1$
+        } else {
+            // Save the partial for next calls
+            this.mPartial = lines[lines.length-1];
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SIGNAL onRequestEnd() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Boolean getResult() {
+        return this.mResult;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void checkExitCode(int exitCode)
+            throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+
+        //Access a subdirectory without permissions returns 1, but this
+        //not must be treated as an error
+        //Ignore exit code 143 (canceled)
+        //Ignore exit code 137 (kill -9)
+        if (exitCode != 0 && exitCode != 1 && exitCode != 143 && exitCode != 137) {
+            throw new ExecutionException(
+                        "exitcode != 0 && != 1 && != 143 && != 137"); //$NON-NLS-1$
+        }
+
+        // Correct
+        this.mResult = Boolean.TRUE;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getOutCompressedFile() {
+        return this.mOutFile;
+    }
+
+    /**
+     * Method that resolves the arguments for the compression
+     *
+     * @return String[] The arguments
+     */
+    private static String[] resolveArguments(CompressionMode mode, String src) {
+        switch (mode) {
+            case GZIP:
+            case BZIP:
+                return new String[]{src};
+            default:
+                return new String[]{};
+        }
+    }
+
+    /**
+     * Method that resolves the output name of the compressed file
+     *
+     * @return String The output name of the compressed file
+     */
+    private static String resolveOutputFile(CompressionMode mode, String src) {
+        return String.format("%s.%s", src, mode.mExtension); //$NON-NLS-1$
+    }
+}
index 08ddbfb..be91a8d 100644 (file)
@@ -47,6 +47,7 @@ import com.cyanogenmod.explorer.commands.ResolveLinkExecutable;
 import com.cyanogenmod.explorer.commands.SIGNAL;
 import com.cyanogenmod.explorer.commands.SendSignalExecutable;
 import com.cyanogenmod.explorer.commands.WriteExecutable;
+import com.cyanogenmod.explorer.commands.shell.CompressCommand.CompressionMode;
 import com.cyanogenmod.explorer.console.CommandNotFoundException;
 import com.cyanogenmod.explorer.console.shell.ShellConsole;
 import com.cyanogenmod.explorer.model.Group;
@@ -487,4 +488,34 @@ public class ShellExecutableCreator implements ExecutableCreator {
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public CompressCommand createCompressExecutable(
+            CompressionMode mode, String dst, String[] src,
+            AsyncResultListener asyncResultListener)
+            throws CommandNotFoundException {
+        try {
+            return new CompressCommand(mode, dst, src, asyncResultListener);
+        } catch (InvalidCommandDefinitionException icdEx) {
+            throw new CommandNotFoundException("CompressCommand", icdEx); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public CompressCommand createCompressExecutable(
+            CompressionMode mode, String src,
+            AsyncResultListener asyncResultListener)
+            throws CommandNotFoundException {
+        try {
+            return new CompressCommand(mode, src, asyncResultListener);
+        } catch (InvalidCommandDefinitionException icdEx) {
+            throw new CommandNotFoundException("CompressCommand", icdEx); //$NON-NLS-1$
+        }
+    }
+
 }
index 29b26d4..f303277 100644 (file)
@@ -18,11 +18,11 @@ package com.cyanogenmod.explorer.util;
 
 import android.content.Context;
 
-import com.cyanogenmod.explorer.commands.AsyncResultExecutable;
 import com.cyanogenmod.explorer.commands.AsyncResultListener;
 import com.cyanogenmod.explorer.commands.ChangeCurrentDirExecutable;
 import com.cyanogenmod.explorer.commands.ChangeOwnerExecutable;
 import com.cyanogenmod.explorer.commands.ChangePermissionsExecutable;
+import com.cyanogenmod.explorer.commands.CompressExecutable;
 import com.cyanogenmod.explorer.commands.CopyExecutable;
 import com.cyanogenmod.explorer.commands.CreateDirExecutable;
 import com.cyanogenmod.explorer.commands.CreateFileExecutable;
@@ -52,6 +52,7 @@ import com.cyanogenmod.explorer.commands.SendSignalExecutable;
 import com.cyanogenmod.explorer.commands.SyncResultExecutable;
 import com.cyanogenmod.explorer.commands.WritableExecutable;
 import com.cyanogenmod.explorer.commands.WriteExecutable;
+import com.cyanogenmod.explorer.commands.shell.CompressCommand.CompressionMode;
 import com.cyanogenmod.explorer.commands.shell.InvalidCommandDefinitionException;
 import com.cyanogenmod.explorer.console.CommandNotFoundException;
 import com.cyanogenmod.explorer.console.Console;
@@ -758,10 +759,9 @@ public final class CommandHelper {
      * @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
-     * @see AsyncResultExecutable
      * @see ExecExecutable
      */
-    public static AsyncResultExecutable exec(
+    public static ExecExecutable exec(
             Context context, String cmd, AsyncResultListener asyncResultListener, Console console)
             throws FileNotFoundException, IOException, ConsoleAllocException,
             NoSuchFileOrDirectory, InsufficientPermissionsException,
@@ -795,10 +795,9 @@ public final class CommandHelper {
      * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
      * @throws ExecutionException If the operation returns a invalid exit code
      * @see SearchResult
-     * @see AsyncResultExecutable
      * @see FindExecutable
      */
-    public static AsyncResultExecutable findFiles(
+    public static FindExecutable findFiles(
             Context context, String directory, Query search,
             AsyncResultListener asyncResultListener, Console console)
             throws FileNotFoundException, IOException, ConsoleAllocException,
@@ -832,10 +831,9 @@ public final class CommandHelper {
      * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
      * @throws ExecutionException If the operation returns a invalid exit code
      * @see FolderUsage
-     * @see AsyncResultExecutable
      * @see FolderUsageExecutable
      */
-    public static AsyncResultExecutable getFolderUsage(
+    public static FolderUsageExecutable getFolderUsage(
             Context context, String directory,
             AsyncResultListener asyncResultListener, Console console)
             throws FileNotFoundException, IOException, ConsoleAllocException,
@@ -1122,9 +1120,9 @@ public final class CommandHelper {
      * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
      * @throws ExecutionException If the operation returns a invalid exit code
      * @see "byte[]"
-     * @see AsyncResultExecutable
+     * @see ReadExecutable
      */
-    public static AsyncResultExecutable read(
+    public static ReadExecutable read(
             Context context, String file,
             AsyncResultListener asyncResultListener, Console console)
             throws FileNotFoundException, IOException, ConsoleAllocException,
@@ -1186,6 +1184,113 @@ public final class CommandHelper {
     }
 
     /**
+     * Method that archive-compress file system objects.
+     *
+     * @param context The current context (needed if console == null)
+     * @param mode The compression mode
+     * @param dst The destination compressed file
+     * @param src The array of source files to compress
+     * @param asyncResultListener The partial result listener
+     * @param console The console in which execute the program.
+     * <code>null</code> to attach to the default console
+     * @return AsyncResultProgram The command executed in background
+     * @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 CompressExecutable
+     */
+    public static CompressExecutable compress(
+            Context context, CompressionMode mode, String dst, String[] src,
+            AsyncResultListener asyncResultListener, Console console)
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
+            ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
+        Console c = ensureConsole(context, console);
+
+        CompressExecutable executable1 =
+                c.getExecutableFactory().newCreator().
+                    createCompressExecutable(mode, dst, src, asyncResultListener);
+
+        // Prior to write to disk the data, ensure that can write to the disk using
+        // createFile method
+        //- Create
+        String compressOutFile = executable1.getOutCompressedFile();
+        CreateFileExecutable executable2 =
+                c.getExecutableFactory().
+                    newCreator().
+                        createCreateFileExecutable(compressOutFile);
+        writableExecute(context, executable2, c);
+        if (executable2.getResult().booleanValue()) {
+            //- Compress
+            execute(context, executable1, c);
+            return executable1;
+        }
+        throw new ExecutionException(
+                String.format("Fail to create file %s", compressOutFile)); //$NON-NLS-1$
+    }
+
+    /**
+     * Method that compress a file system object.
+     *
+     * @param context The current context (needed if console == null)
+     * @param mode The compression mode
+     * @param src The file to compress
+     * @param asyncResultListener The partial result listener
+     * @param console The console in which execute the program.
+     * <code>null</code> to attach to the default console
+     * @return AsyncResultProgram The command executed in background
+     * @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 CompressExecutable
+     */
+    public static CompressExecutable compress(
+            Context context, CompressionMode mode, String src,
+            AsyncResultListener asyncResultListener, Console console)
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
+            ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
+        Console c = ensureConsole(context, console);
+
+        CompressExecutable executable1 =
+                c.getExecutableFactory().newCreator().
+                    createCompressExecutable(mode, src, asyncResultListener);
+
+        // Prior to write to disk the data, ensure that can write to the disk using
+        // createFile method
+        //- Create
+        String compressOutFile = executable1.getOutCompressedFile();
+        CreateFileExecutable executable2 =
+                c.getExecutableFactory().
+                    newCreator().
+                        createCreateFileExecutable(compressOutFile);
+        writableExecute(context, executable2, c);
+        if (executable2.getResult().booleanValue()) {
+            //- Compress
+            execute(context, executable1, c);
+            return executable1;
+        }
+        throw new ExecutionException(
+                String.format("Fail to create file %s", compressOutFile)); //$NON-NLS-1$
+    }
+
+    /**
      * Method that re-execute the command.
      *
      * @param context The current context (needed if console == null)
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/CompressCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/CompressCommandTest.java
new file mode 100644 (file)
index 0000000..cfb0821
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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 android.os.Environment;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.CompressExecutable;
+import com.cyanogenmod.explorer.commands.shell.CompressCommand.CompressionMode;
+import com.cyanogenmod.explorer.util.CommandHelper;
+
+/**
+ * A class for testing folder usage command.
+ *
+ * @see CompressCommand
+ */
+public class CompressCommandTest extends AbstractConsoleTest {
+
+    private static final String TAG = "CompressCommandTest"; //$NON-NLS-1$
+
+    private static final String TAR_OUTFILE =
+            Environment.getDataDirectory().getAbsolutePath() + "/test.tar"; //$NON-NLS-1$
+    private static final String TAR_GZIP_OUTFILE = TAR_OUTFILE + ".gz"; //$NON-NLS-1$
+    private static final String TAR_BZIP_OUTFILE = TAR_OUTFILE + ".bz2"; //$NON-NLS-1$
+
+    private static final String[] ARCHIVE_DATA =
+        {
+            Environment.getDataDirectory().getAbsolutePath() + "/misc", //$NON-NLS-1$
+            Environment.getRootDirectory().getAbsolutePath() + "/build.prop" //$NON-NLS-1$
+        };
+    private static final String COMPRESS_DATA_SRC =
+            Environment.getRootDirectory().getAbsolutePath() + "/build.prop"; //$NON-NLS-1$
+    private static final String COMPRESS_DATA_DST =
+            Environment.getDataDirectory().getAbsolutePath() + "/build.prop"; //$NON-NLS-1$
+
+    /**
+     * @hide
+     */
+    final Object mSync = new Object();
+    /**
+     * @hide
+     */
+    boolean mNewPartialData;
+    /**
+     * @hide
+     */
+    boolean mNormalEnd;
+    /**
+     * @hide
+     */
+    boolean mResult;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRootConsoleNeeded() {
+        return true;
+    }
+
+    /**
+     * Method that performs the test of archive data in TAR format.
+     *
+     * @throws Exception If test failed
+     */
+    @LargeTest
+    public void testArchiveTAR() throws Exception {
+        testArchiveAndCompress(CompressionMode.NONE, TAR_OUTFILE);
+    }
+
+    /**
+     * Method that performs the test of archive and compress data in GZIP format.
+     *
+     * @throws Exception If test failed
+     */
+    @LargeTest
+    public void testArchiveCompressGZIP() throws Exception {
+        testArchiveAndCompress(CompressionMode.GZIP, TAR_GZIP_OUTFILE);
+    }
+
+    /**
+     * Method that performs the test of archive and compress data in BZIP format.
+     *
+     * @throws Exception If test failed
+     */
+    @LargeTest
+    public void testArchiveCompressBZIP() throws Exception {
+        testArchiveAndCompress(CompressionMode.BZIP, TAR_BZIP_OUTFILE);
+    }
+
+    /**
+     * Method that performs the test of compress data in GZIP format.
+     *
+     * @throws Exception If test failed
+     */
+    @LargeTest
+    public void testCompressGZIP() throws Exception {
+        testCompress(CompressionMode.GZIP);
+    }
+
+    /**
+     * Method that performs the test of compress data in BZIP format.
+     *
+     * @throws Exception If test failed
+     */
+    @LargeTest
+    public void testCompressBZIP() throws Exception {
+        testCompress(CompressionMode.BZIP);
+    }
+
+    /**
+     * Method that archive and compress data.
+     *
+     * @throws Exception If test failed
+     */
+    private void testArchiveAndCompress(CompressionMode mode, String dst) throws Exception {
+        try {
+            this.mNewPartialData = false;
+            this.mNormalEnd = false;
+            CompressExecutable cmd =
+                    CommandHelper.compress(
+                        getContext(), mode, dst, ARCHIVE_DATA, new AsyncResultListener() {
+                            public void onAsyncStart() {
+                                /**NON BLOCK**/
+                            }
+                            public void onAsyncEnd(boolean canceled) {
+                                synchronized (CompressCommandTest.this.mSync) {
+                                    CompressCommandTest.this.mNormalEnd = true;
+                                    CompressCommandTest.this.mSync.notify();
+                                }
+                            }
+                            public void onAsyncExitCode(int exitCode) {
+                                /**NON BLOCK**/
+                            }
+                            public void onException(Exception cause) {
+                                fail(String.valueOf(cause));
+                            }
+                            public void onPartialResult(Object result) {
+                                CompressCommandTest.this.mNewPartialData = true;
+                                Log.d(TAG, (String)result);
+                            }
+                       }, getConsole());
+            synchronized (CompressCommandTest.this.mSync) {
+                CompressCommandTest.this.mSync.wait(60000L);
+            }
+            try {
+                if (!this.mNormalEnd && cmd != null && cmd.isCancelable() && !cmd.isCanceled()) {
+                    cmd.cancel();
+                }
+            } catch (Exception e) {/**NON BLOCK**/}
+
+            // Wait for result
+            Thread.sleep(500L);
+
+            assertTrue("no new partial data", this.mNewPartialData); //$NON-NLS-1$
+            assertNotNull("cmd != null", cmd); //$NON-NLS-1$
+            if (cmd != null) {
+                assertTrue("return != true", cmd.getResult().booleanValue()); //$NON-NLS-1$
+            }
+        } finally {
+            try {
+                CommandHelper.deleteFile(getContext(), dst, getConsole());
+            } catch (Exception e) {/**NON BLOCK**/}
+        }
+    }
+
+    /**
+     * Method that compress data.
+     *
+     * @throws Exception If test failed
+     */
+    private void testCompress(CompressionMode mode) throws Exception {
+        CompressExecutable cmd = null;
+        try {
+            // Copy a file to the folder of the test
+            CommandHelper.copy(getContext(), COMPRESS_DATA_SRC, COMPRESS_DATA_DST, getConsole());
+
+            this.mNewPartialData = false;
+            this.mNormalEnd = false;
+            cmd =
+                    CommandHelper.compress(
+                        getContext(), mode, COMPRESS_DATA_DST, new AsyncResultListener() {
+                            public void onAsyncStart() {
+                                /**NON BLOCK**/
+                            }
+                            public void onAsyncEnd(boolean canceled) {
+                                synchronized (CompressCommandTest.this.mSync) {
+                                    CompressCommandTest.this.mNormalEnd = true;
+                                    CompressCommandTest.this.mSync.notify();
+                                }
+                            }
+                            public void onAsyncExitCode(int exitCode) {
+                                /**NON BLOCK**/
+                            }
+                            public void onException(Exception cause) {
+                                fail(String.valueOf(cause));
+                            }
+                            public void onPartialResult(Object result) {
+                                CompressCommandTest.this.mNewPartialData = true;
+                                Log.d(TAG, (String)result);
+                            }
+                       }, getConsole());
+            synchronized (CompressCommandTest.this.mSync) {
+                CompressCommandTest.this.mSync.wait(15000L);
+            }
+            try {
+                if (!this.mNormalEnd && cmd != null && cmd.isCancelable() && !cmd.isCanceled()) {
+                    cmd.cancel();
+                }
+            } catch (Exception e) {/**NON BLOCK**/}
+
+            // Wait for result
+            Thread.sleep(500L);
+
+            assertNotNull("cmd != null", cmd); //$NON-NLS-1$
+            if (cmd != null) {
+                assertTrue("return != true", cmd.getResult().booleanValue()); //$NON-NLS-1$
+            }
+        } finally {
+            if (cmd != null) {
+                try {
+                    CommandHelper.deleteFile(
+                            this.mContext, COMPRESS_DATA_DST, getConsole());
+                } catch (Exception e) {/**NON BLOCK**/}
+                try {
+                    CommandHelper.deleteFile(
+                            this.mContext, cmd.getOutCompressedFile(), getConsole());
+                } catch (Exception e) {/**NON BLOCK**/}
+            }
+        }
+    }
+
+}