/* * 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.filemanager.commands.shell; import com.cyanogenmod.filemanager.commands.AsyncResultListener; import com.cyanogenmod.filemanager.commands.CompressExecutable; import com.cyanogenmod.filemanager.commands.SIGNAL; import com.cyanogenmod.filemanager.console.CommandNotFoundException; import com.cyanogenmod.filemanager.console.ExecutionException; import com.cyanogenmod.filemanager.console.InsufficientPermissionsException; import com.cyanogenmod.filemanager.preferences.CompressionMode; import com.cyanogenmod.filemanager.util.FileHelper; import java.io.File; /** * A class for compress file system objects * * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?tar"} * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?gzip"} * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?bzip2"} */ public class CompressCommand extends AsyncResultProgram implements CompressExecutable { /** * An enumeration of implemented compression modes. */ private enum Mode { /** * Archive using Tar algorithm */ A_TAR(TAR_ID, "", CompressionMode.A_TAR), //$NON-NLS-1$ /** * Archive and compress using Gzip algorithm */ AC_GZIP(TAR_ID, "z", CompressionMode.AC_GZIP), //$NON-NLS-1$ /** * Archive and compress using Gzip algorithm */ AC_GZIP2(TAR_ID, "z", CompressionMode.AC_GZIP2), //$NON-NLS-1$ /** * Archive and compress using Bzip algorithm */ AC_BZIP(TAR_ID, "j", CompressionMode.AC_BZIP), //$NON-NLS-1$ /** * Compress using Gzip algorithm */ C_GZIP(GZIP_ID, "z", CompressionMode.C_GZIP), //$NON-NLS-1$ /** * Compress using Bzip algorithm */ C_BZIP(BZIP_ID, "j", CompressionMode.C_BZIP); //$NON-NLS-1$ final String mId; final String mFlag; final CompressionMode mMode; /** * Constructor of <code>Mode</code> * * @param id The command identifier * @param flag The tar compression flag * @param mode The compression mode */ private Mode(String id, String flag, CompressionMode mode) { this.mId = id; this.mFlag = flag; this.mMode = mode; } /** * Method that return the mode from his compression mode * * @param mode The compression mode * @return Mode The mode */ public static Mode fromCompressionMode(CompressionMode mode) { Mode[] modes = Mode.values(); int cc = modes.length; for (int i = 0; i < cc; i++) { if (modes[i].mMode.compareTo(mode) == 0) { return modes[i]; } } return null; } } private static final String TAR_ID = "tar"; //$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.fromCompressionMode(mode).mFlag, dst}); //Convert the arguments from absolute to relative addExpandedArguments( convertAbsolutePathsToRelativePaths(dst, 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.fromCompressionMode(mode).mId, asyncResultListener, resolveArguments(mode, src)); if (Mode.fromCompressionMode(mode).mMode.mArchive) { throw new InvalidCommandDefinitionException( "Unsupported compression mode"); //$NON-NLS-1$ } // 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 cancelled) { // Send the last 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 int cc = lines.length; for (int i = 0; i < cc-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 { //Ignore exit code 143 (cancelled) //Ignore exit code 137 (kill -9) if (exitCode != 0 && exitCode != 1 && exitCode != 143 && exitCode != 137) { throw new ExecutionException( "exitcode != 0 && != 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 C_GZIP: case C_BZIP: return new String[]{src}; default: return new String[]{}; } } /** * Method that resolves the output path of the compressed file * * @return String The output path of the compressed file */ private static String resolveOutputFile(CompressionMode mode, String src) { return String.format("%s.%s", src, mode.mExtension); //$NON-NLS-1$ } /** * Method that converts the absolute paths of the source files to relative paths * * @param dst The destination compressed file * @param src The source uncompressed files * @return String[] The array of relative paths */ private static String[] convertAbsolutePathsToRelativePaths(String dst, String[] src) { File parent = new File(dst).getParentFile(); String p = File.separator; if (parent != null) { p = parent.getAbsolutePath(); } // Converts every path String[] out = new String[src.length]; int cc = src.length; for (int i = 0; i < cc; i++) { out[i] = FileHelper.toRelativePath(src[i], p); } return out; } }