com.gh.bmd.jrt.android.v4.core.DefaultContextRoutine.java Source code

Java tutorial

Introduction

Here is the source code for com.gh.bmd.jrt.android.v4.core.DefaultContextRoutine.java

Source

/*
 * 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.gh.bmd.jrt.android.v4.core;

import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;

import com.gh.bmd.jrt.android.builder.ContextRoutineBuilder.CacheStrategyType;
import com.gh.bmd.jrt.android.builder.ContextRoutineBuilder.ClashResolutionType;
import com.gh.bmd.jrt.android.invocation.ContextInvocation;
import com.gh.bmd.jrt.android.routine.ContextRoutine;
import com.gh.bmd.jrt.android.runner.Runners;
import com.gh.bmd.jrt.builder.RoutineConfiguration;
import com.gh.bmd.jrt.builder.RoutineConfiguration.OrderType;
import com.gh.bmd.jrt.common.InvocationException;
import com.gh.bmd.jrt.common.InvocationInterruptedException;
import com.gh.bmd.jrt.common.RoutineException;
import com.gh.bmd.jrt.core.AbstractRoutine;
import com.gh.bmd.jrt.invocation.Invocation;
import com.gh.bmd.jrt.log.Logger;
import com.gh.bmd.jrt.runner.Execution;

import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * Routine implementation delegating to Android loaders the asynchronous processing.
 * <p/>
 * Created by davide on 1/10/15.
 *
 * @param <INPUT>  the input data type.
 * @param <OUTPUT> the output data type.
 */
class DefaultContextRoutine<INPUT, OUTPUT> extends AbstractRoutine<INPUT, OUTPUT>
        implements ContextRoutine<INPUT, OUTPUT> {

    private final Object[] mArgs;

    private final CacheStrategyType mCacheStrategyType;

    private final ClashResolutionType mClashResolutionType;

    private final Constructor<? extends ContextInvocation<INPUT, OUTPUT>> mConstructor;

    private final WeakReference<Object> mContext;

    private final int mInvocationId;

    private final OrderType mOrderType;

    /**
     * Constructor.
     *
     * @param configuration  the routine configuration.
     * @param context        the context reference.
     * @param invocationId   the invocation ID.
     * @param resolutionType the clash resolution type.
     * @param strategyType   the result cache strategy type.
     * @param constructor    the invocation constructor.
     * @param args           the invocation constructor arguments.
     * @throws java.lang.IllegalArgumentException if at least one of the parameter is invalid.
     * @throws java.lang.NullPointerException     if any of the specified non-null parameter is
     *                                            null.
     */
    @SuppressWarnings("ConstantConditions")
    DefaultContextRoutine(@Nonnull final RoutineConfiguration configuration,
            @Nonnull final WeakReference<Object> context, final int invocationId,
            @Nullable final ClashResolutionType resolutionType, @Nullable final CacheStrategyType strategyType,
            @Nonnull final Constructor<? extends ContextInvocation<INPUT, OUTPUT>> constructor,
            @Nonnull final Object[] args) {

        super(configuration);

        if (context == null) {

            throw new NullPointerException("the context must not be null");
        }

        if (constructor == null) {

            throw new NullPointerException("the invocation constructor must not be null");
        }

        if (args == null) {

            throw new NullPointerException("the invocation constructor array of arguments must not be null");
        }

        mContext = context;
        mInvocationId = invocationId;
        mClashResolutionType = (resolutionType == null) ? ClashResolutionType.ABORT_THAT_INPUT : resolutionType;
        mCacheStrategyType = (strategyType == null) ? CacheStrategyType.CLEAR : strategyType;
        mConstructor = constructor;
        mArgs = args;
        mOrderType = configuration.getOutputOrderOr(null);
    }

    @Override
    public void purge() {

        super.purge();
        final WeakReference<Object> context = mContext;

        if (context.get() != null) {

            final Class<? extends ContextInvocation<INPUT, OUTPUT>> invocationClass = mConstructor
                    .getDeclaringClass();
            Runners.mainRunner().run(new PurgeExecution(context, mInvocationId, invocationClass, mArgs), 0,
                    TimeUnit.MILLISECONDS);
        }
    }

    @Nonnull
    @Override
    protected Invocation<INPUT, OUTPUT> convertInvocation(final boolean async,
            @Nonnull final Invocation<INPUT, OUTPUT> invocation) {

        try {

            invocation.onDestroy();

        } catch (final InvocationInterruptedException e) {

            throw e;

        } catch (final Throwable ignored) {

            getLogger().wrn(ignored, "ignoring exception while destroying invocation instance");
        }

        return newInvocation(async);
    }

    @Nonnull
    @Override
    protected Invocation<INPUT, OUTPUT> newInvocation(final boolean async) {

        final Logger logger = getLogger();

        if (async) {

            return new LoaderInvocation<INPUT, OUTPUT>(mContext, mInvocationId, mClashResolutionType,
                    mCacheStrategyType, mConstructor, mArgs, mOrderType, logger);
        }

        final Object context = mContext.get();

        if (context == null) {

            throw new IllegalStateException("the routine context has been destroyed");
        }

        final Context appContext;

        if (context instanceof FragmentActivity) {

            final FragmentActivity activity = (FragmentActivity) context;
            appContext = activity.getApplicationContext();

        } else if (context instanceof Fragment) {

            final Fragment fragment = (Fragment) context;
            appContext = fragment.getActivity().getApplicationContext();

        } else {

            throw new IllegalArgumentException("invalid context type: " + context.getClass().getCanonicalName());
        }

        try {

            final Constructor<? extends ContextInvocation<INPUT, OUTPUT>> constructor = mConstructor;
            logger.dbg("creating a new instance of class: %s", constructor.getDeclaringClass());
            final ContextInvocation<INPUT, OUTPUT> invocation = constructor.newInstance(mArgs);
            invocation.onContext(appContext);
            return invocation;

        } catch (final RoutineException e) {

            logger.err(e, "error creating the invocation instance");
            throw e;

        } catch (final Throwable t) {

            logger.err(t, "error creating the invocation instance");
            throw new InvocationException(t);
        }
    }

    public void purge(@Nullable final INPUT input) {

        final WeakReference<Object> context = mContext;

        if (context.get() != null) {

            final Class<? extends ContextInvocation<INPUT, OUTPUT>> invocationClass = mConstructor
                    .getDeclaringClass();
            final List<INPUT> inputList = Collections.singletonList(input);
            final PurgeInputsExecution<INPUT> execution = new PurgeInputsExecution<INPUT>(context, mInvocationId,
                    invocationClass, mArgs, inputList);
            Runners.mainRunner().run(execution, 0, TimeUnit.MILLISECONDS);
        }
    }

    public void purge(@Nullable final INPUT... inputs) {

        final WeakReference<Object> context = mContext;

        if (context.get() != null) {

            final Class<? extends ContextInvocation<INPUT, OUTPUT>> invocationClass = mConstructor
                    .getDeclaringClass();
            final List<INPUT> inputList = (inputs == null) ? Collections.<INPUT>emptyList() : Arrays.asList(inputs);
            final PurgeInputsExecution<INPUT> execution = new PurgeInputsExecution<INPUT>(context, mInvocationId,
                    invocationClass, mArgs, inputList);
            Runners.mainRunner().run(execution, 0, TimeUnit.MILLISECONDS);
        }
    }

    public void purge(@Nullable final Iterable<? extends INPUT> inputs) {

        final WeakReference<Object> context = mContext;

        if (context.get() != null) {

            final List<INPUT> inputList;

            if (inputs == null) {

                inputList = Collections.emptyList();

            } else {

                inputList = new ArrayList<INPUT>();

                for (final INPUT input : inputs) {

                    inputList.add(input);
                }
            }

            final Class<? extends ContextInvocation<INPUT, OUTPUT>> invocationClass = mConstructor
                    .getDeclaringClass();
            final PurgeInputsExecution<INPUT> execution = new PurgeInputsExecution<INPUT>(context, mInvocationId,
                    invocationClass, mArgs, inputList);
            Runners.mainRunner().run(execution, 0, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * Execution implementation purging all loaders with a specific invocation class.
     */
    private static class PurgeExecution implements Execution {

        private final WeakReference<Object> mContext;

        private final Object[] mInvocationArgs;

        private final Class<?> mInvocationClass;

        private final int mInvocationId;

        /**
         * Constructor.
         *
         * @param context         the context reference.
         * @param invocationId    the invocation ID.
         * @param invocationClass the invocation class.
         * @param invocationArgs  the invocation constructor arguments.
         */
        private PurgeExecution(@Nonnull final WeakReference<Object> context, final int invocationId,
                @Nonnull final Class<?> invocationClass, @Nonnull final Object[] invocationArgs) {

            mContext = context;
            mInvocationId = invocationId;
            mInvocationClass = invocationClass;
            mInvocationArgs = invocationArgs;
        }

        public void run() {

            final Object context = mContext.get();

            if (context != null) {

                LoaderInvocation.purgeLoaders(context, mInvocationId, mInvocationClass, mInvocationArgs);
            }
        }
    }

    /**
     * Execution implementation purging the loader with a specific invocation class and inputs.
     *
     * @param <INPUT> the input data type.
     */
    private static class PurgeInputsExecution<INPUT> implements Execution {

        private final WeakReference<Object> mContext;

        private final List<INPUT> mInputs;

        private final Object[] mInvocationArgs;

        private final Class<?> mInvocationClass;

        private final int mInvocationId;

        /**
         * Constructor.
         *
         * @param context         the context reference.
         * @param invocationId    the invocation ID.
         * @param invocationClass the invocation class.
         * @param invocationArgs  the invocation constructor arguments.
         * @param inputs          the list of inputs.
         */
        private PurgeInputsExecution(@Nonnull final WeakReference<Object> context, final int invocationId,
                @Nonnull final Class<?> invocationClass, @Nonnull final Object[] invocationArgs,
                @Nonnull final List<INPUT> inputs) {

            mContext = context;
            mInvocationId = invocationId;
            mInvocationClass = invocationClass;
            mInvocationArgs = invocationArgs;
            mInputs = inputs;
        }

        public void run() {

            final Object context = mContext.get();

            if (context != null) {

                LoaderInvocation.purgeLoader(context, mInvocationId, mInvocationClass, mInvocationArgs, mInputs);
            }
        }
    }
}