<commit_before>package com.xtreme.rest.service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

import com.xtreme.threading.AuxiliaryExecutor;
import com.xtreme.threading.RequestIdentifier;

/**
 * A Task consists of two components: a network request, and a processing request.</br>
 * The network request occurs in {@link #onExecuteNetworkRequest(Context)} and is used to download data from a network,
 * and is then parsed into the generic specified. The processing request then processes the data that was just downloaded
 * and stores it if necessary.</br>
 * </br>
 * Please keep in mind that no CPU-intensive processing should happen in {@link #onExecuteNetworkRequest(Context)}, as that
 * may cause UI lag. All of that work should be done in {@link #onExecuteProcessingRequest(Context, Object)}.
 * 
 * @param <T> The type downloaded, parsed, and then returned from {@link #onExecuteNetworkRequest(Context)}. In most cases it is a model
 * class, but sometimes it may be a {@link String}.
 */
public abstract class Task<T> implements NetworkHandler<T>, NetworkObserver<T>, ProcessingHandler<T>, ProcessingObserver<T> {

	private static final class Messages {
		private static final String NO_HANDLER = "Cannot execute request. No handler found.";
	}
	
	private final Set<Task<?>> mPrerequisites = new HashSet<Task<?>>();
	private final Set<Task<?>> mDependants = new HashSet<Task<?>>();
	private final List<ServiceError> mErrors = new ArrayList<ServiceError>();
	private final Object mTaskLock = new Object();
			
	private Priority mPriority = Priority.MEDIUM;
	private TaskObserver mObserver;
	private RequestHandler mHandler;
	private RequestIdentifier<?> mIdentifier;
	private Context mContext;
	
	@Override
	public final RequestIdentifier<?> getIdentifier() {
		return mIdentifier;
	}

	public void setContext(final Context context) {
		mContext = context;
	}

	public void setPriority(final Priority priority) {
		mPriority = priority;
	}

	public void setTaskObserver(final TaskObserver observer) {
		mObserver = observer;
	}
	
	public void setRequestHandler(final RequestHandler handler) {
		mHandler = handler;
	}
	
	public void execute() {
		mIdentifier = onCreateIdentifier();
		checkPrerequisites();
	}

	private void checkPrerequisites() {
		if (allPrerequisitesComplete()) {
			checkExecution();
		}	
	}

	private boolean allPrerequisitesComplete() {
		synchronized (mTaskLock) {
			return mPrerequisites.isEmpty();
		}
	}

	private void checkExecution() {
		if (!mErrors.isEmpty()) {
			final ServiceError error = mErrors.get(0); 
			notifyFailure(error);
		} else {
			notifyStarted();
			startNetworkRequest();
		}
	}

	// ======================================================
	
	/**
	 * This method must return a globally unique {@link RequestIdentifier}. This is used within the {@link AuxiliaryExecutor}
	 * and ensures that if more than one {@link Task} is processing the same data, only one actually executes and the results
	 * are then shared among all {@link Operation}s "executing" that {@link Task}.
	 * 
	 * @return The unique {@link RequestIdentifier}
	 */
	public abstract RequestIdentifier<?> onCreateIdentifier();
	
	/**
	 * This method is meant to execute network-dependent code (or anything that yields the {@link Thread} frequently and is
	 * light on the CPU). For example, this is where your model would be downloaded and parsed from the API.</br>
	 * </br>
	 * Note that you should try to parse straight from the {@link InputStream} instead of converting it to a {@link String}
	 * and then parsing that. This is to avoid doing any CPU-intensive processing in this method, such as parsing or string
	 * processing. If it is absolutely necessary, then use this method to only convert the {@link InputStream} to a {@link String},
	 * and then process the {@link String} and parse it in {@link #onExecuteProcessingRequest(Context, Object)}. In this
	 * case, you would want to make the generic for this class a {@link String}.</br>
	 * </br>
	 * All network processing must happen synchronously within this method.
	 * 
	 * @param context The context in which this {@link Task} is running
	 * @return The result of the network request
	 * @throws Exception
	 */
	public abstract T onExecuteNetworkRequest(Context context) throws Exception;

	/**
	 * This method is meant to execute CPU-intensive processing requests. For example, this is where one would process a {@link String} or
	 * insert data into a {@link ContentProvider} backed by a {@link SQLiteDatabase} via the {@link ContentResolver}.</br>
	 * </br>
	 * All processing must happen synchronously within this method.
	 * 
	 * @param context The context in which this {@link Task} is running
	 * @param data The result returned from {@link #onExecuteNetworkRequest(Context)}
	 * @throws Exception
	 */
	public abstract void onExecuteProcessingRequest(Context context, T data) throws Exception;

	// ======================================================

	/**
	 * Adds a prerequisite. This {@link Task} will wait until all prerequisites are finished before being executed.
	 * 
	 * @param task The prerequisite {@link Task}
	 */
	public final void addPrerequisite(final Task<?> task) {
		synchronized (mTaskLock) {
			if (!mPrerequisites.contains(task)) {
				mPrerequisites.add(task);
				task.addDependant(this);
			}
		}
	}

	/**
	 * Adds a dependent. The provided {@link Task} will not execute until this, and all its other prerequisites
	 * are finished.
	 * 
	 * @param task The dependent {@link Task}
	 */
	public final void addDependant(final Task<?> task) {
		synchronized (mTaskLock) {
			if (!mDependants.contains(task)) {
				mDependants.add(task);
				task.addPrerequisite(this);
			}<commit_msg>JW - updated the javadoc for adding a dependant to a task<commit_after>package com.xtreme.rest.service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

import com.xtreme.threading.AuxiliaryExecutor;
import com.xtreme.threading.RequestIdentifier;

/**
 * A Task consists of two components: a network request, and a processing request.</br>
 * The network request occurs in {@link #onExecuteNetworkRequest(Context)} and is used to download data from a network,
 * and is then parsed into the generic specified. The processing request then processes the data that was just downloaded
 * and stores it if necessary.</br>
 * </br>
 * Please keep in mind that no CPU-intensive processing should happen in {@link #onExecuteNetworkRequest(Context)}, as that
 * may cause UI lag. All of that work should be done in {@link #onExecuteProcessingRequest(Context, Object)}.
 * 
 * @param <T> The type downloaded, parsed, and then returned from {@link #onExecuteNetworkRequest(Context)}. In most cases it is a model
 * class, but sometimes it may be a {@link String}.
 */
public abstract class Task<T> implements NetworkHandler<T>, NetworkObserver<T>, ProcessingHandler<T>, ProcessingObserver<T> {

	private static final class Messages {
		private static final String NO_HANDLER = "Cannot execute request. No handler found.";
	}
	
	private final Set<Task<?>> mPrerequisites = new HashSet<Task<?>>();
	private final Set<Task<?>> mDependants = new HashSet<Task<?>>();
	private final List<ServiceError> mErrors = new ArrayList<ServiceError>();
	private final Object mTaskLock = new Object();
			
	private Priority mPriority = Priority.MEDIUM;
	private TaskObserver mObserver;
	private RequestHandler mHandler;
	private RequestIdentifier<?> mIdentifier;
	private Context mContext;
	
	@Override
	public final RequestIdentifier<?> getIdentifier() {
		return mIdentifier;
	}

	public void setContext(final Context context) {
		mContext = context;
	}

	public void setPriority(final Priority priority) {
		mPriority = priority;
	}

	public void setTaskObserver(final TaskObserver observer) {
		mObserver = observer;
	}
	
	public void setRequestHandler(final RequestHandler handler) {
		mHandler = handler;
	}
	
	public void execute() {
		mIdentifier = onCreateIdentifier();
		checkPrerequisites();
	}

	private void checkPrerequisites() {
		if (allPrerequisitesComplete()) {
			checkExecution();
		}	
	}

	private boolean allPrerequisitesComplete() {
		synchronized (mTaskLock) {
			return mPrerequisites.isEmpty();
		}
	}

	private void checkExecution() {
		if (!mErrors.isEmpty()) {
			final ServiceError error = mErrors.get(0); 
			notifyFailure(error);
		} else {
			notifyStarted();
			startNetworkRequest();
		}
	}

	// ======================================================
	
	/**
	 * This method must return a globally unique {@link RequestIdentifier}. This is used within the {@link AuxiliaryExecutor}
	 * and ensures that if more than one {@link Task} is processing the same data, only one actually executes and the results
	 * are then shared among all {@link Operation}s "executing" that {@link Task}.
	 * 
	 * @return The unique {@link RequestIdentifier}
	 */
	public abstract RequestIdentifier<?> onCreateIdentifier();
	
	/**
	 * This method is meant to execute network-dependent code (or anything that yields the {@link Thread} frequently and is
	 * light on the CPU). For example, this is where your model would be downloaded and parsed from the API.</br>
	 * </br>
	 * Note that you should try to parse straight from the {@link InputStream} instead of converting it to a {@link String}
	 * and then parsing that. This is to avoid doing any CPU-intensive processing in this method, such as parsing or string
	 * processing. If it is absolutely necessary, then use this method to only convert the {@link InputStream} to a {@link String},
	 * and then process the {@link String} and parse it in {@link #onExecuteProcessingRequest(Context, Object)}. In this
	 * case, you would want to make the generic for this class a {@link String}.</br>
	 * </br>
	 * All network processing must happen synchronously within this method.
	 * 
	 * @param context The context in which this {@link Task} is running
	 * @return The result of the network request
	 * @throws Exception
	 */
	public abstract T onExecuteNetworkRequest(Context context) throws Exception;

	/**
	 * This method is meant to execute CPU-intensive processing requests. For example, this is where one would process a {@link String} or
	 * insert data into a {@link ContentProvider} backed by a {@link SQLiteDatabase} via the {@link ContentResolver}.</br>
	 * </br>
	 * All processing must happen synchronously within this method.
	 * 
	 * @param context The context in which this {@link Task} is running
	 * @param data The result returned from {@link #onExecuteNetworkRequest(Context)}
	 * @throws Exception
	 */
	public abstract void onExecuteProcessingRequest(Context context, T data) throws Exception;

	// ======================================================

	/**
	 * Adds a prerequisite. This {@link Task} will wait until all prerequisites are finished before being executed.
	 * 
	 * @param task The prerequisite {@link Task}
	 */
	public final void addPrerequisite(final Task<?> task) {
		synchronized (mTaskLock) {
			if (!mPrerequisites.contains(task)) {
				mPrerequisites.add(task);
				task.addDependant(this);
			}
		}
	}

	/**
	 * Adds a dependant. The {@link Task} will notify all dependants when it has completed so they may execute.
	 * 
	 * @param task The dependant {@link Task}
	 */
	public final void addDependant(final Task<?> task) {
		synchronized (mTaskLock) {
			if (!mDependants.contains(task)) {
				mDependants.add(task);
				task.addPrerequisite(this);
			}
{'fraction_non_alphanumeric': 0.11022632020117351, 'fraction_numerical': 0.00016764459346186087, 'mean_word_length': 4.445458694659973, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4643427', 'n_tokens_mistral': 3384, 'n_tokens_neox': 3063, 'n_words': 1281}