/*
* 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.SIGNAL;
import com.cyanogenmod.filemanager.commands.UncompressExecutable;
import com.cyanogenmod.filemanager.console.CommandNotFoundException;
import com.cyanogenmod.filemanager.console.ExecutionException;
import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
import com.cyanogenmod.filemanager.preferences.UncompressionMode;
import com.cyanogenmod.filemanager.util.FileHelper;
import java.io.File;
/**
* A class for uncompress file system objects.
*
* {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?tar"}
* {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?unzip"}
* {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?gunzip"}
* {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?bunzip2"}
* {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?xz"}
*/
public class UncompressCommand extends AsyncResultProgram implements UncompressExecutable {
/**
* An enumeration of implemented uncompression modes.
*/
private enum Mode {
/**
* Uncompress using Tar algorithm
*/
A_UNTAR(UNTAR_ID, "", UncompressionMode.A_UNTAR), //$NON-NLS-1$
/**
* Uncompress using Tar algorithm
*/
A_UNZIP(UNZIP_ID, "", UncompressionMode.A_UNZIP), //$NON-NLS-1$
/**
* Uncompress using Gzip algorithm
*/
AC_GUNZIP(UNTAR_ID, "z", UncompressionMode.AC_GUNZIP), //$NON-NLS-1$
/**
* Uncompress using Gzip algorithm
*/
AC_GUNZIP2(UNTAR_ID, "z", UncompressionMode.AC_GUNZIP2), //$NON-NLS-1$
/**
* Uncompress using Bzip algorithm
*/
AC_BUNZIP(UNTAR_ID, "j", UncompressionMode.AC_BUNZIP), //$NON-NLS-1$
/**
* Uncompress using Lzma algorithm
*/
AC_UNLZMA(UNTAR_ID, "a", UncompressionMode.AC_UNLZMA), //$NON-NLS-1$
/**
* Uncompress using Gzip algorithm
*/
C_GUNZIP(GUNZIP_ID, "", UncompressionMode.C_GUNZIP), //$NON-NLS-1$
/**
* Uncompress using Bzip algorithm
*/
C_BUNZIP(BUNZIP_ID, "", UncompressionMode.C_BUNZIP), //$NON-NLS-1$
/**
* Uncompress using Lzma algorithm
*/
C_UNLZMA(UNLZMA_ID, "", UncompressionMode.C_UNLZMA), //$NON-NLS-1$
/**
* Uncompress using Unix compress algorithm
*/
C_UNCOMPRESS(UNCOMPRESS_ID, "", UncompressionMode.C_UNCOMPRESS), //$NON-NLS-1$
/**
* Uncompress using Unix compress algorithm
*/
C_UNXZ(UNXZ_ID, "", UncompressionMode.C_UNXZ); //$NON-NLS-1$
final String mId;
final String mFlag;
UncompressionMode mMode;
/**
* Constructor of <code>Mode</code>
*
* @param id The command identifier
* @param flag The tar compression flag
* @param mode The uncompressed mode
*/
private Mode(String id, String flag, UncompressionMode mode) {
this.mId = id;
this.mFlag = flag;
this.mMode = mode;
}
}
private static final String UNTAR_ID = "untar"; //$NON-NLS-1$
private static final String UNZIP_ID = "unzip"; //$NON-NLS-1$
private static final String GUNZIP_ID = "gunzip"; //$NON-NLS-1$
private static final String BUNZIP_ID = "bunzip"; //$NON-NLS-1$
private static final String UNLZMA_ID = "unlzma"; //$NON-NLS-1$
private static final String UNCOMPRESS_ID = "uncompress"; //$NON-NLS-1$
private static final String UNXZ_ID = "unxz"; //$NON-NLS-1$
private Boolean mResult;
private String mPartial;
private final String mOutFile;
private final boolean mIsArchive;
/**
* Constructor of <code>UncompressCommand</code>.<br/>
* <br/>
* <ul>
* <li>For archive and archive-compressed files, the file is extracted in a directory
* of the current location of the file with the name of the file without the extension.</li>
* <li>For compressed files, the file is extracted in the same directory in a file without
* the extension, and the source file is deleted.</li>
* </ul>
*
* @param src The archive-compressed file
* @param dst The destination file of folder (if null this method resolve with the best
* fit based on the src)
* @param asyncResultListener The partial result listener
* @throws InvalidCommandDefinitionException If the command has an invalid definition
*/
public UncompressCommand(
String src, String dst, AsyncResultListener asyncResultListener)
throws InvalidCommandDefinitionException {
super(resolveId(src), asyncResultListener, resolveArguments(src, dst));
// Check that have a valid
Mode mode = getMode(src);
if (mode == null) {
throw new InvalidCommandDefinitionException(
"Unsupported uncompress mode"); //$NON-NLS-1$
}
// Retrieve information about the uncompress process
if (dst != null) {
this.mOutFile = dst;
} else {
this.mOutFile = resolveOutputFile(src);
}
this.mIsArchive = mode.mMode.mArchive;
}
/**
* {@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
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 {
//Ignore exit code 143 (cancelled)
//Ignore exit code 137 (kill -9)
if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
throw new ExecutionException(
"exitcode != 0 && != 1 && != 143 && != 137"); //$NON-NLS-1$
}
// Correct
this.mResult = Boolean.TRUE;
}
/**
* {@inheritDoc}
*/
@Override
public String getOutUncompressedFile() {
return this.mOutFile;
}
/**
* {@inheritDoc}
*/
@Override
public boolean IsArchive() {
return this.mIsArchive;
}
/**
* Method that resolves the identifier to use as command
*
* @param src The compressed file
* @return String The identifier of the command
*/
private static String resolveId(String src) {
Mode mode = getMode(src);
if (mode != null) {
return mode.mId;
}
return ""; //$NON-NLS-1$
}
/**
* Method that resolves the arguments for the uncompression
*
* @param src The source file
* @param dst The destination file
* @return String[] The arguments
*/
private static String[] resolveArguments(String src, String dst) {
String out = dst;
if (out == null) {
out = resolveOutputFile(src);
}
Mode mode = getMode(src);
if (mode != null) {
switch (mode) {
case A_UNTAR:
case AC_GUNZIP:
case AC_GUNZIP2:
case AC_BUNZIP:
case AC_UNLZMA:
return new String[]{mode.mFlag, out, src};
case A_UNZIP:
return new String[]{out, src};
case C_GUNZIP:
case C_BUNZIP:
case C_UNLZMA:
case C_UNCOMPRESS:
case C_UNXZ:
return new String[]{src};
default:
break;
}
}
return new String[]{};
}
/**
* Method that resolves the output path of the uncompressed file
*
* @return String The output path of the uncompressed file
*/
private static String resolveOutputFile(String src) {
String name = new File(FileHelper.getName(src)).getName();
File dst = new File(new File(src).getParent(), name);
return dst.getAbsolutePath();
}
/**
* Method that returns the uncompression mode from the compressed file
*
* @param src The compressed file
* @return Mode The uncompression mode. <code>null</code> if no mode found
*/
private static Mode getMode(String src) {
String extension = FileHelper.getExtension(src);
Mode[] modes = Mode.values();
int cc = modes.length;
for (int i = 0; i < cc; i++) {
Mode mode = modes[i];
if (mode.mMode.mExtension.compareToIgnoreCase(extension) == 0) {
return mode;
}
}
return null;
}
}