diff options
| author | jruesga <jorge@ruesga.com> | 2012-10-26 03:08:49 +0200 |
|---|---|---|
| committer | jruesga <jorge@ruesga.com> | 2012-10-26 03:08:49 +0200 |
| commit | 6be595d8eeba247a9fb9614a2c314d1e1a184f3d (patch) | |
| tree | a013a200bca62336f9c3553005c14f76163f66f5 /src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java | |
| parent | 7370b7164125a63dfa8d40aa0de04c24754c6a64 (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.java | 404 |
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(); + } + } + } + } + +} |
