<commit_before>package com.octo.android.robospice;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import roboguice.util.temp.Ln;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import com.octo.android.robospice.SpiceService.SpiceServiceBinder;
import com.octo.android.robospice.persistence.DurationInMillis;
import com.octo.android.robospice.persistence.exception.CacheLoadingException;
import com.octo.android.robospice.request.CachedSpiceRequest;
import com.octo.android.robospice.request.SpiceRequest;
import com.octo.android.robospice.request.listener.RequestListener;

/**
 * The instances of this class allow to acces the {@link SpiceService}. <br/>
 * They are tied to activities and obtain a local binding to the
 * {@link SpiceService}. When binding occurs, the {@link SpiceManager} will send
 * commadnds to the {@link SpiceService}, to execute requests, clear cache,
 * prevent listeners from being called and so on. Basically, all features of the
 * {@link SpiceService} are accessible from the {@link SpiceManager}. It acts as
 * an asynchronous proxy : every call to a {@link SpiceService} method is
 * asynchronous and will occur as soon as possible when the {@link SpiceManager}
 * successfully binds to the service.
 * @author jva
 * @author sni
 * @author mwa
 */

/*
 * Note to maintainers : This class is quite complex and requires background
 * knowledge in multi-threading & local service binding in android. Thx to Henri
 * Tremblay (from EasyMock) for his happy code review.
 */
public class SpiceManager implements Runnable {

    /** The class of the {@link SpiceService} to bind to. */
    private final Class<? extends SpiceService> spiceServiceClass;

    /** A reference on the {@link SpiceService} obtained by local binding. */
    private SpiceService spiceService;
    /** {@link SpiceService} binder. */
    private SpiceServiceConnection spiceServiceConnection = new SpiceServiceConnection();

    /** The contextWeakReference used to bind to the service from. */
    private WeakReference<Context> contextWeakReference;

    /**
     * Whether or not {@link SpiceManager} is started. Must be volatile to
     * ensure multi-thread consistency.
     */
    private volatile boolean isStopped = true;

    /** The queue of requests to be sent to the service. */
    private final BlockingQueue<CachedSpiceRequest<?>> requestQueue = new LinkedBlockingQueue<CachedSpiceRequest<?>>();

    /**
     * The list of all requests that have not yet been passed to the service.
     * All iterations must be synchronized.
     */
    private final Map<CachedSpiceRequest<?>, Set<RequestListener<?>>> mapRequestToLaunchToRequestListener = Collections
        .synchronizedMap(new IdentityHashMap<CachedSpiceRequest<?>, Set<RequestListener<?>>>());

    /**
     * The list of all requests that have already been passed to the service.
     * All iterations must be synchronized.
     */
    private final Map<CachedSpiceRequest<?>, Set<RequestListener<?>>> mapPendingRequestToRequestListener = Collections
        .synchronizedMap(new IdentityHashMap<CachedSpiceRequest<?>, Set<RequestListener<?>>>());

    private final ExecutorService executorService = Executors.newSingleThreadExecutor();

    /**
     * Lock used to synchronize binding to / unbinding from the
     * {@link SpiceService}.
     */
    private final ReentrantLock lockAcquireService = new ReentrantLock();
    /** A monitor to ensure service is bound before accessing it. */
    private final Condition conditionServiceBound = lockAcquireService.newCondition();
    /** A monitor to ensure service is unbound. */
    private final Condition conditionServiceUnbound = lockAcquireService.newCondition();

    /**
     * Lock used to synchronize transmission of requests to the
     * {@link SpiceService}.
     */
    private final ReentrantLock lockSendRequestsToService = new ReentrantLock();

    /** Thread running runnable code. */
    protected Thread runner;

    /** Reacts to service processing of requests. */
    private final RequestRemoverSpiceServiceListener removerSpiceServiceListener = new RequestRemoverSpiceServiceListener();

    /**
     * Whether or not we are unbinding (to prevent unbinding twice. Must be
     * volatile to ensure multi-thread consistency.
     */
    private volatile boolean isUnbinding = false;

    // ============================================================================================
    // THREAD BEHAVIOR
    // ============================================================================================

    /**
     * Creates a {@link SpiceManager}. Typically this occurs in the construction
     * of an Activity or Fragment. This method will check if the service to bind
     * to has been properly declared in AndroidManifest.
     * @param spiceServiceClass
     *            the service class to bind to.
     */
    public SpiceManager(final Class<? extends SpiceService> spiceServiceClass) {
        this.spiceServiceClass = spiceServiceClass;
    }

    /**
     * Start the {@link SpiceManager}. It will bind asynchronously to the
     * {@link SpiceService}.
     * @param contextWeakReference
     *            a contextWeakReference that will be used to bind to the
     *            service. Typically, the Activity or Fragment that needs to
     *            interact with the {@link SpiceService}.
     */
    public synchronized void start(final Context context) {
        this.contextWeakReference = new WeakReference<Context>(context);
        if (runner != null) {
            throw new IllegalStateException("Already started.");
        } else {

            // start the binding to the service
            runner = new Thread(this);
            isStopped = false;
            runner.start();

            Ln.d("SpiceManager started.");
        }
    }

    /**
     * Method is synchronized with {@link #start(Context)}.
     * @return whether or not the {@link SpiceManager} is started.
     */
    public synchronized boolean isStarted() {
        return !isStopped;
    }

    @Override
    public void run() {
        checkServiceIsProperlyDeclaredInAndroidManifest(contextWeakReference.get());
        // start the service it is not started yet.
        Context context = contextWeakReference.get();
        if (context != null) {
            final Intent intent = new Intent(context, spiceServiceClass);
            context.startService(intent);
        } else {
            Ln.d("Service was not started as Activity died prematurely");
        }

        bindToService(contextWeakReference.get());

        try {
            waitForServiceToBeBound();
            if (spiceService == null) {
                return;
            }
            while (!isStopped) {
                try {
                    sendRequestToService(requestQueue.take());
                } catch (final InterruptedException ex) {
                    Ln.d(ex, "Interrupted while waiting for new request.");
                }
            }
        } catch (final InterruptedException e) {
            Ln.d(e, "Interrupted while waiting for acquiring service.");
        } finally {
            unbindFromService(contextWeakReference.get());
        }
    }

    private void sendRequestToService(final CachedSpiceRequest<?> spiceRequest) {
        lockSendRequestsToService.lock();
        try {
            if (spiceRequest != null) {
                final Set<RequestListener<?>> listRequestListener = mapRequestToLaunchToRequestListener.remove(spiceRequest);
                mapPendingRequestToRequestListener.put(spiceRequest, listRequestListener);
                Ln.d("Sending request to service : " + spiceRequest.getClass().getSimpleName());
                spiceService.addRequest(spiceRequest, listRequestListener);
            }
        } finally {
            lockSendRequestsToService.unlock();
        }
    }

    /**
     * Stops the {@link SpiceManager}. It will unbind from {@link SpiceService}.
     * All request listeners that had been registered to listen to
     * {@link SpiceRequest}s sent from this {@link SpiceManager} will be
     * unregistered. None of them will be notified with the results of their<commit_msg>do not log the InterruptedException stacktrace. See #21<commit_after>package com.octo.android.robospice;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import roboguice.util.temp.Ln;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import com.octo.android.robospice.SpiceService.SpiceServiceBinder;
import com.octo.android.robospice.persistence.DurationInMillis;
import com.octo.android.robospice.persistence.exception.CacheLoadingException;
import com.octo.android.robospice.request.CachedSpiceRequest;
import com.octo.android.robospice.request.SpiceRequest;
import com.octo.android.robospice.request.listener.RequestListener;

/**
 * The instances of this class allow to acces the {@link SpiceService}. <br/>
 * They are tied to activities and obtain a local binding to the
 * {@link SpiceService}. When binding occurs, the {@link SpiceManager} will send
 * commadnds to the {@link SpiceService}, to execute requests, clear cache,
 * prevent listeners from being called and so on. Basically, all features of the
 * {@link SpiceService} are accessible from the {@link SpiceManager}. It acts as
 * an asynchronous proxy : every call to a {@link SpiceService} method is
 * asynchronous and will occur as soon as possible when the {@link SpiceManager}
 * successfully binds to the service.
 * @author jva
 * @author sni
 * @author mwa
 */

/*
 * Note to maintainers : This class is quite complex and requires background
 * knowledge in multi-threading & local service binding in android. Thx to Henri
 * Tremblay (from EasyMock) for his happy code review.
 */
public class SpiceManager implements Runnable {

    /** The class of the {@link SpiceService} to bind to. */
    private final Class<? extends SpiceService> spiceServiceClass;

    /** A reference on the {@link SpiceService} obtained by local binding. */
    private SpiceService spiceService;
    /** {@link SpiceService} binder. */
    private SpiceServiceConnection spiceServiceConnection = new SpiceServiceConnection();

    /** The contextWeakReference used to bind to the service from. */
    private WeakReference<Context> contextWeakReference;

    /**
     * Whether or not {@link SpiceManager} is started. Must be volatile to
     * ensure multi-thread consistency.
     */
    private volatile boolean isStopped = true;

    /** The queue of requests to be sent to the service. */
    private final BlockingQueue<CachedSpiceRequest<?>> requestQueue = new LinkedBlockingQueue<CachedSpiceRequest<?>>();

    /**
     * The list of all requests that have not yet been passed to the service.
     * All iterations must be synchronized.
     */
    private final Map<CachedSpiceRequest<?>, Set<RequestListener<?>>> mapRequestToLaunchToRequestListener = Collections
        .synchronizedMap(new IdentityHashMap<CachedSpiceRequest<?>, Set<RequestListener<?>>>());

    /**
     * The list of all requests that have already been passed to the service.
     * All iterations must be synchronized.
     */
    private final Map<CachedSpiceRequest<?>, Set<RequestListener<?>>> mapPendingRequestToRequestListener = Collections
        .synchronizedMap(new IdentityHashMap<CachedSpiceRequest<?>, Set<RequestListener<?>>>());

    private final ExecutorService executorService = Executors.newSingleThreadExecutor();

    /**
     * Lock used to synchronize binding to / unbinding from the
     * {@link SpiceService}.
     */
    private final ReentrantLock lockAcquireService = new ReentrantLock();
    /** A monitor to ensure service is bound before accessing it. */
    private final Condition conditionServiceBound = lockAcquireService.newCondition();
    /** A monitor to ensure service is unbound. */
    private final Condition conditionServiceUnbound = lockAcquireService.newCondition();

    /**
     * Lock used to synchronize transmission of requests to the
     * {@link SpiceService}.
     */
    private final ReentrantLock lockSendRequestsToService = new ReentrantLock();

    /** Thread running runnable code. */
    protected Thread runner;

    /** Reacts to service processing of requests. */
    private final RequestRemoverSpiceServiceListener removerSpiceServiceListener = new RequestRemoverSpiceServiceListener();

    /**
     * Whether or not we are unbinding (to prevent unbinding twice. Must be
     * volatile to ensure multi-thread consistency.
     */
    private volatile boolean isUnbinding = false;

    // ============================================================================================
    // THREAD BEHAVIOR
    // ============================================================================================

    /**
     * Creates a {@link SpiceManager}. Typically this occurs in the construction
     * of an Activity or Fragment. This method will check if the service to bind
     * to has been properly declared in AndroidManifest.
     * @param spiceServiceClass
     *            the service class to bind to.
     */
    public SpiceManager(final Class<? extends SpiceService> spiceServiceClass) {
        this.spiceServiceClass = spiceServiceClass;
    }

    /**
     * Start the {@link SpiceManager}. It will bind asynchronously to the
     * {@link SpiceService}.
     * @param contextWeakReference
     *            a contextWeakReference that will be used to bind to the
     *            service. Typically, the Activity or Fragment that needs to
     *            interact with the {@link SpiceService}.
     */
    public synchronized void start(final Context context) {
        this.contextWeakReference = new WeakReference<Context>(context);
        if (runner != null) {
            throw new IllegalStateException("Already started.");
        } else {

            // start the binding to the service
            runner = new Thread(this);
            isStopped = false;
            runner.start();

            Ln.d("SpiceManager started.");
        }
    }

    /**
     * Method is synchronized with {@link #start(Context)}.
     * @return whether or not the {@link SpiceManager} is started.
     */
    public synchronized boolean isStarted() {
        return !isStopped;
    }

    @Override
    public void run() {
        checkServiceIsProperlyDeclaredInAndroidManifest(contextWeakReference.get());
        // start the service it is not started yet.
        Context context = contextWeakReference.get();
        if (context != null) {
            final Intent intent = new Intent(context, spiceServiceClass);
            context.startService(intent);
        } else {
            Ln.d("Service was not started as Activity died prematurely");
        }

        bindToService(contextWeakReference.get());

        try {
            waitForServiceToBeBound();
            if (spiceService == null) {
                return;
            }
            while (!isStopped) {
                try {
                    sendRequestToService(requestQueue.take());
                } catch (final InterruptedException ex) {
                    Ln.d("Interrupted while waiting for new request.");
                }
            }
        } catch (final InterruptedException e) {
            Ln.d(e, "Interrupted while waiting for acquiring service.");
        } finally {
            unbindFromService(contextWeakReference.get());
        }
    }

    private void sendRequestToService(final CachedSpiceRequest<?> spiceRequest) {
        lockSendRequestsToService.lock();
        try {
            if (spiceRequest != null) {
                final Set<RequestListener<?>> listRequestListener = mapRequestToLaunchToRequestListener.remove(spiceRequest);
                mapPendingRequestToRequestListener.put(spiceRequest, listRequestListener);
                Ln.d("Sending request to service : " + spiceRequest.getClass().getSimpleName());
                spiceService.addRequest(spiceRequest, listRequestListener);
            }
        } finally {
            lockSendRequestsToService.unlock();
        }
    }

    /**
     * Stops the {@link SpiceManager}. It will unbind from {@link SpiceService}.
     * All request listeners that had been registered to listen to
     * {@link SpiceRequest}s sent from this {@link SpiceManager} will be
     * unregistered. None of them will be notified with the results of their
{'fraction_non_alphanumeric': 0.10601799775028121, 'fraction_numerical': 0.0001124859392575928, 'mean_word_length': 3.12360853432282, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '22160', 'n_tokens_mistral': 4523, 'n_tokens_neox': 4379, 'n_words': 1439}