aboutsummaryrefslogtreecommitdiff
path: root/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
diff options
context:
space:
mode:
authorjruesga <jorge@ruesga.com>2012-10-26 03:08:49 +0200
committerjruesga <jorge@ruesga.com>2012-10-26 03:08:49 +0200
commit6be595d8eeba247a9fb9614a2c314d1e1a184f3d (patch)
treea013a200bca62336f9c3553005c14f76163f66f5 /src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
parent7370b7164125a63dfa8d40aa0de04c24754c6a64 (diff)
Change application name to 'File Manager' (issue #20)
Full refactoring of package from explorer to filemanager
Diffstat (limited to 'src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java')
-rw-r--r--src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java b/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
new file mode 100644
index 0000000..1b1f07d
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
@@ -0,0 +1,404 @@
+/*
+ * 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.AsyncResultExecutable;
+import com.cyanogenmod.filemanager.commands.AsyncResultListener;
+import com.cyanogenmod.filemanager.commands.SIGNAL;
+import com.cyanogenmod.filemanager.util.FileHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An abstract class that allow the consumption of partial data. Commands
+ * can parse the results while this are still retrieving.
+ */
+public abstract class AsyncResultProgram
+ extends Program implements AsyncResultExecutable, AsyncResultProgramListener {
+
+ /**
+ * @hide
+ */
+ static final Byte STDIN = new Byte((byte)0);
+ /**
+ * @hide
+ */
+ static final Byte STDERR = new Byte((byte)1);
+
+ private final AsyncResultListener mAsyncResultListener;
+ private AsyncResultProgramThread mWorkerThread;
+ /**
+ * @hide
+ */
+ final List<String> mPartialData;
+ /**
+ * @hide
+ */
+ final List<Byte> mPartialDataType;
+ private final Object mSync = new Object();
+ /**
+ * @hide
+ */
+ final Object mTerminateSync = new Object();
+
+ private boolean mCancelled;
+ private OnCancelListener mOnCancelListener;
+ private OnEndListener mOnEndListener;
+
+ private StringBuffer mTempBuffer;
+
+ /**
+ * @Constructor of <code>AsyncResultProgram</code>.
+ *
+ * @param id The resource identifier of the command
+ * @param asyncResultListener The partial result listener
+ * @param args Arguments of the command (will be formatted with the arguments from
+ * the command definition)
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public AsyncResultProgram(
+ String id, AsyncResultListener asyncResultListener, String... args)
+ throws InvalidCommandDefinitionException {
+ this(id, true, asyncResultListener, args);
+ }
+
+ /**
+ * @Constructor of <code>AsyncResultProgram</code>.
+ *
+ * @param id The resource identifier of the command
+ * @param prepare Indicates if the argument must be prepared
+ * @param asyncResultListener The partial result listener
+ * @param args Arguments of the command (will be formatted with the arguments from
+ * the command definition)
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public AsyncResultProgram(
+ String id, boolean prepare, AsyncResultListener asyncResultListener, String... args)
+ throws InvalidCommandDefinitionException {
+ super(id, prepare, args);
+ this.mAsyncResultListener = asyncResultListener;
+ this.mPartialData = Collections.synchronizedList(new ArrayList<String>());
+ this.mPartialDataType = Collections.synchronizedList(new ArrayList<Byte>());
+ this.mTempBuffer = new StringBuffer();
+ this.mOnCancelListener = null;
+ this.mOnEndListener = null;
+ this.mCancelled = false;
+ }
+
+ /**
+ * Method that communicates that a new partial result parse will start.
+ * @hide
+ */
+ public final void onRequestStartParsePartialResult() {
+ this.mWorkerThread = new AsyncResultProgramThread(this.mSync);
+ this.mWorkerThread.start();
+
+ //Notify start to command class
+ this.onStartParsePartialResult();
+
+ //If a listener is defined, then send the start event
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onAsyncStart();
+ }
+ }
+
+ /**
+ * Method that communicates that partial result is ended and no new result
+ * will be received.
+ *
+ * @param cancelled If the program was cancelled
+ * @hide
+ */
+ public final void onRequestEndParsePartialResult(boolean cancelled) {
+ synchronized (this.mSync) {
+ this.mWorkerThread.mAlive = false;
+ this.mSync.notify();
+ }
+ synchronized (this.mTerminateSync) {
+ try {
+ this.mSync.wait();
+ } catch (Exception e) {
+ /**NON BLOCK**/
+ }
+ try {
+ if (this.mWorkerThread.isAlive()) {
+ this.mWorkerThread.interrupt();
+ }
+ } catch (Exception e) {
+ /**NON BLOCK**/
+ }
+ }
+
+ //Notify end to command class
+ this.onEndParsePartialResult(cancelled);
+
+ //If a listener is defined, then send the start event
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onAsyncEnd(cancelled);
+ }
+ }
+
+ /**
+ * Method that communicates the exit code of the program
+ *
+ * @param exitCode The exit code of the program
+ * @hide
+ */
+ public final void onRequestExitCode(int exitCode) {
+ //If a listener is defined, then send the start event
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onAsyncExitCode(exitCode);
+ }
+ }
+
+ /**
+ * Method that parse the result of a program invocation.
+ *
+ * @param partialIn A partial standard input buffer (incremental buffer)
+ * @hide
+ */
+ public final void onRequestParsePartialResult(String partialIn) {
+ synchronized (this.mSync) {
+ String data = partialIn;
+ if (parseOnlyCompleteLines()) {
+ int pos = partialIn.lastIndexOf(FileHelper.NEWLINE);
+ if (pos == -1) {
+ //Save partial data
+ this.mTempBuffer.append(partialIn);
+ return;
+ }
+
+ //Retrieve the data
+ data = this.mTempBuffer.append(partialIn.substring(0, pos + 1)).toString();
+ }
+
+ this.mPartialDataType.add(STDIN);
+ this.mPartialData.add(data);
+ this.mTempBuffer = new StringBuffer();
+ this.mSync.notify();
+ }
+ }
+
+ /**
+ * Method that parse the error result of a program invocation.
+ *
+ * @param partialErr A partial standard err buffer (incremental buffer)
+ * @hide
+ */
+ public final void parsePartialErrResult(String partialErr) {
+ synchronized (this.mSync) {
+ String data = partialErr;
+ if (parseOnlyCompleteLines()) {
+ int pos = partialErr.lastIndexOf(FileHelper.NEWLINE);
+ if (pos == -1) {
+ //Save partial data
+ this.mTempBuffer.append(partialErr);
+ return;
+ }
+
+ //Retrieve the data
+ data = this.mTempBuffer.append(partialErr.substring(0, pos + 1)).toString();
+ }
+
+ this.mPartialDataType.add(STDERR);
+ this.mPartialData.add(data);
+ this.mTempBuffer = new StringBuffer();
+ this.mSync.notify();
+ }
+ }
+
+ /**
+ * Method that returns if the <code>onParsePartialResult</code> method will
+ * be called only complete lines are filled.
+ *
+ * @return boolean if the <code>onParsePartialResult</code> method will
+ * be called only complete lines are filled
+ */
+ @SuppressWarnings("static-method")
+ public boolean parseOnlyCompleteLines() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean isCancelled() {
+ return this.mCancelled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean cancel() {
+ //Is't cancellable by definition?
+ if (!isCancellable()) {
+ return false;
+ }
+
+ //Stop the thread
+ synchronized (this.mSync) {
+ this.mWorkerThread.mAlive = false;
+ this.mSync.notify();
+ }
+
+ //Notify cancellation
+ if (this.mOnCancelListener != null) {
+ this.mCancelled = this.mOnCancelListener.onCancel();
+ return this.mCancelled;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean end() {
+ // Internally this method do the same things that cancel method, but invokes
+ // onEnd instead of onCancel
+
+ //Is't cancellable by definition?
+ if (!isCancellable()) {
+ return false;
+ }
+
+ //Stop the thread
+ synchronized (this.mSync) {
+ this.mWorkerThread.mAlive = false;
+ this.mSync.notify();
+ }
+
+ //Notify ending
+ SIGNAL signal = onRequestEnd();
+ if (this.mOnEndListener != null) {
+ if (signal == null) {
+ this.mCancelled = this.mOnEndListener.onEnd();
+ } else {
+ this.mCancelled = this.mOnEndListener.onSendSignal(signal);
+ }
+ return this.mCancelled;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void setOnCancelListener(OnCancelListener onCancelListener) {
+ this.mOnCancelListener = onCancelListener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void setOnEndListener(OnEndListener onEndListener) {
+ this.mOnEndListener = onEndListener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ //By defect an asynchronous command is cancellable
+ return true;
+ }
+
+ /**
+ * Method that returns if the command is expected to finalize by it self, or needs
+ * a call to end method.
+ *
+ * @return boolean If the command is expected to finalize by it self.
+ */
+ @SuppressWarnings("static-method")
+ public boolean isExpectEnd() {
+ return true;
+ }
+
+ /**
+ * An internal class for process partial results sequentially in a
+ * secure way.
+ */
+ private class AsyncResultProgramThread extends Thread {
+ boolean mAlive = true;
+ private final Object mSyncObj;
+
+ /**
+ * Constructor of <code>AsyncResultProgramThread</code>.
+ *
+ * @param sync The synchronized object
+ */
+ AsyncResultProgramThread(Object sync) {
+ super();
+ this.mSyncObj = sync;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void run() {
+ try {
+ this.mAlive = true;
+ while (this.mAlive) {
+ synchronized (this.mSyncObj) {
+ this.mSyncObj.wait();
+ while (AsyncResultProgram.this.mPartialData.size() > 0) {
+ if (!this.mAlive) {
+ return;
+ }
+ Byte type = AsyncResultProgram.this.mPartialDataType.remove(0);
+ String data = AsyncResultProgram.this.mPartialData.remove(0);
+ try {
+ if (type.compareTo(STDIN) == 0) {
+ AsyncResultProgram.this.onParsePartialResult(data);
+ } else {
+ AsyncResultProgram.this.onParseErrorPartialResult(data);
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ /**NON BLOCK**/
+
+ } finally {
+ this.mAlive = false;
+ synchronized (AsyncResultProgram.this.mTerminateSync) {
+ AsyncResultProgram.this.mTerminateSync.notify();
+ }
+ }
+ }
+ }
+
+}