org.jboss.aerogear.android.pipe.loader.LoaderAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.aerogear.android.pipe.loader.LoaderAdapter.java

Source

/**
 * JBoss, Home of Professional Open Source
 * Copyright Red Hat, Inc., and individual contributors.
 *
 * 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 org.jboss.aerogear.android.pipe.loader;

import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.HashMap;

import org.jboss.aerogear.android.core.Callback;
import org.jboss.aerogear.android.core.ReadFilter;
import org.jboss.aerogear.android.pipe.callback.AbstractActivityCallback;
import org.jboss.aerogear.android.pipe.callback.AbstractFragmentCallback;
import org.jboss.aerogear.android.pipe.LoaderPipe;
import org.jboss.aerogear.android.pipe.Pipe;
import org.jboss.aerogear.android.pipe.PipeHandler;
import org.jboss.aerogear.android.pipe.RequestBuilder;
import org.jboss.aerogear.android.pipe.ResponseParser;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.util.ArrayList;

import java.util.Arrays;
import java.util.Map;
import org.jboss.aerogear.android.pipe.http.HeaderAndBody;
import org.jboss.aerogear.android.core.reflection.Scan;
import org.jboss.aerogear.android.pipe.callback.AbstractSupportFragmentCallback;

/**
 * This class wraps a Pipe in an asynchronous Loader.
 * 
 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@SuppressWarnings({ "rawtypes", "unchecked" })
public class LoaderAdapter<T> implements LoaderPipe<T>, LoaderManager.LoaderCallbacks<HeaderAndBody> {

    private static final String TAG = LoaderAdapter.class.getSimpleName();
    private final Handler handler;
    private Map<String, List<Integer>> idsForNamedPipes;

    private enum Methods {

        READ, READ_ID, SAVE, REMOVE
    }

    private final Context applicationContext;
    private Fragment fragment;
    private Activity activity;
    private android.support.v4.app.Fragment supportFragment;
    private final Pipe<T> pipe;
    private final LoaderManager manager;
    private final String name;
    private final RequestBuilder<T> requestBuilder;
    private final ResponseParser<T> responseParser;

    public LoaderAdapter(Activity activity, Pipe<T> pipe, String name) {
        this.pipe = pipe;
        this.requestBuilder = pipe.getRequestBuilder();
        this.responseParser = pipe.getResponseParser();
        this.manager = activity.getLoaderManager();
        this.applicationContext = activity.getApplicationContext();
        this.name = name;
        this.handler = new Handler(Looper.getMainLooper());
        this.activity = activity;
    }

    public LoaderAdapter(Fragment fragment, Context applicationContext, Pipe<T> pipe, String name) {
        this.pipe = pipe;
        this.manager = fragment.getLoaderManager();
        this.requestBuilder = pipe.getRequestBuilder();
        this.responseParser = pipe.getResponseParser();
        this.applicationContext = applicationContext;
        this.name = name;
        this.handler = new Handler(Looper.getMainLooper());
        this.fragment = fragment;
    }

    public LoaderAdapter(android.support.v4.app.Fragment supportFragment, Context applicationContext, Pipe<T> pipe,
            String name) {
        this.pipe = pipe;
        this.manager = supportFragment.getActivity().getLoaderManager();
        this.requestBuilder = pipe.getRequestBuilder();
        this.responseParser = pipe.getResponseParser();
        this.applicationContext = applicationContext;
        this.name = name;
        this.handler = new Handler(Looper.getMainLooper());
        this.supportFragment = supportFragment;
    }

    @Override
    public URL getUrl() {
        return pipe.getUrl();
    }

    @Override
    public void read(String idx, Callback<T> callback) {
        ReadFilter filter = new ReadFilter();
        filter.setLinkUri(URI.create(idx));

        int id = Arrays.hashCode(new Object[] { name, filter, callback });
        Bundle bundle = new Bundle();
        bundle.putSerializable(CALLBACK, callback);
        bundle.putSerializable(FILTER, filter);
        bundle.putSerializable(METHOD, Methods.READ_ID);
        manager.initLoader(id, bundle, this);
    }

    @Override
    public void read(Callback<List<T>> callback) {
        int id = Arrays.hashCode(new Object[] { name, callback });
        Bundle bundle = new Bundle();
        bundle.putSerializable(CALLBACK, callback);
        bundle.putSerializable(FILTER, null);
        bundle.putSerializable(METHOD, Methods.READ);
        manager.initLoader(id, bundle, this);
    }

    @Override
    public void read(ReadFilter filter, Callback<List<T>> callback) {
        int id = Arrays.hashCode(new Object[] { name, filter, callback });
        Bundle bundle = new Bundle();
        bundle.putSerializable(CALLBACK, callback);
        bundle.putSerializable(FILTER, filter);
        bundle.putSerializable(METHOD, Methods.READ);
        manager.initLoader(id, bundle, this);
    }

    @Override
    public void save(T item, Callback<T> callback) {
        int id = Arrays.hashCode(new Object[] { name, item, callback });
        Bundle bundle = new Bundle();
        bundle.putSerializable(CALLBACK, callback);
        bundle.putSerializable(ITEM, requestBuilder.getBody(item));
        bundle.putString(SAVE_ID, Scan.findIdValueIn(item));
        bundle.putSerializable(METHOD, Methods.SAVE);
        manager.initLoader(id, bundle, this);
    }

    @Override
    public void remove(String toRemoveId, Callback<Void> callback) {
        int id = Arrays.hashCode(new Object[] { name, toRemoveId, callback });
        Bundle bundle = new Bundle();
        bundle.putSerializable(CALLBACK, callback);
        bundle.putSerializable(REMOVE_ID, toRemoveId);
        bundle.putSerializable(METHOD, Methods.REMOVE);
        manager.initLoader(id, bundle, this);
    }

    @Override
    public PipeHandler<T> getHandler() {
        return pipe.getHandler();
    }

    @Override
    public RequestBuilder<T> getRequestBuilder() {
        return requestBuilder;
    }

    @Override
    public ResponseParser<T> getResponseParser() {
        return responseParser;
    }

    @Override
    public Class<T> getKlass() {
        return pipe.getKlass();
    }

    @Override
    public Loader<HeaderAndBody> onCreateLoader(int id, Bundle bundle) {
        addId(name, id);

        Methods method = (Methods) bundle.get(METHOD);
        Callback callback = (Callback) bundle.get(CALLBACK);
        verifyCallback(callback);
        AbstractPipeLoader loader = null;
        switch (method) {
        case READ: {
            ReadFilter filter = (ReadFilter) bundle.get(FILTER);
            loader = new ReadLoader(applicationContext, callback, pipe.getHandler(), filter, this);
        }
            break;
        case READ_ID: {
            ReadFilter filter = (ReadFilter) bundle.get(FILTER);
            loader = new IdReadLoader(applicationContext, callback, pipe.getHandler(), filter, this);
        }
            break;
        case REMOVE: {
            String toRemove = bundle.getString(REMOVE_ID, "-1");
            loader = new RemoveLoader(applicationContext, callback, pipe.getHandler(), toRemove);
        }
            break;
        case SAVE: {
            byte[] data = bundle.getByteArray(ITEM);
            String dataId = bundle.getString(SAVE_ID);
            loader = new SaveLoader(applicationContext, callback, pipe.getHandler(), data, dataId);
        }
            break;
        }
        return loader;
    }

    @Override
    public void onLoadFinished(Loader<HeaderAndBody> loader, final HeaderAndBody data) {
        if (!(loader instanceof AbstractPipeLoader)) {
            Log.e(TAG, "Adapter is listening to loaders which it doesn't support");
            throw new IllegalStateException("Adapter is listening to loaders which it doesn't support");
        } else {
            final AbstractPipeLoader<HeaderAndBody> modernLoader = (AbstractPipeLoader<HeaderAndBody>) loader;
            Object object = null;
            if (!modernLoader.hasException() && data != null && data.getBody() != null) {
                object = extractObject(data, modernLoader);
            }

            handler.post(new CallbackHandler<T>(this, modernLoader, object));
        }
    }

    @Override
    public void onLoaderReset(Loader<HeaderAndBody> loader) {
        Log.e(TAG, loader.toString());

    }

    @Override
    public void reset() {
        if (idsForNamedPipes == null) {
            idsForNamedPipes = new HashMap<String, List<Integer>>();
        }

        List<Integer> ids = idsForNamedPipes.get(name);
        if (ids != null) {
            for (Integer id : ids) {
                if (id != null) {
                    Loader loader = manager.getLoader(id);
                    if (loader != null) {
                        manager.destroyLoader(id);
                    }
                }
            }
        }

        idsForNamedPipes.put(name, new ArrayList<Integer>());
    }

    @Override
    public void setLoaderIds(Map<String, List<Integer>> idsForNamedPipes) {
        this.idsForNamedPipes = idsForNamedPipes;
    }

    private void fragmentSuccess(Callback typelessCallback, Object data) {
        AbstractFragmentCallback callback = (AbstractFragmentCallback) typelessCallback;
        callback.setFragment(fragment);
        callback.onSuccess(data);
        callback.setFragment(null);
    }

    private void fragmentFailure(Callback typelessCallback, Exception exception) {
        AbstractFragmentCallback callback = (AbstractFragmentCallback) typelessCallback;
        callback.setFragment(fragment);
        callback.onFailure(exception);
        callback.setFragment(null);
    }

    private void activitySuccess(Callback typelessCallback, Object data) {
        AbstractActivityCallback callback = (AbstractActivityCallback) typelessCallback;
        callback.setActivity(activity);
        callback.onSuccess(data);
        callback.setActivity(null);
    }

    private void activityFailure(Callback typelessCallback, Exception exception) {
        AbstractActivityCallback callback = (AbstractActivityCallback) typelessCallback;
        callback.setActivity(activity);
        callback.onFailure(exception);
        callback.setActivity(null);
    }

    private void supportFragmentSuccess(Callback typelessCallback, Object data) {
        AbstractSupportFragmentCallback callback = (AbstractSupportFragmentCallback) typelessCallback;
        callback.setSupportFragment(supportFragment);
        callback.onSuccess(data);
        callback.setSupportFragment(null);
    }

    private void supportFragmentFailure(Callback typelessCallback, Exception exception) {
        AbstractSupportFragmentCallback callback = (AbstractSupportFragmentCallback) typelessCallback;
        callback.setSupportFragment(supportFragment);
        callback.onFailure(exception);
        callback.setSupportFragment(null);
    }

    private Object extractObject(HeaderAndBody data, AbstractPipeLoader<HeaderAndBody> modernLoader) {
        List results = responseParser.handleResponse(data, getKlass());

        if (results == null || results.size() == 0) {
            return results;
        } else if ((modernLoader instanceof SaveLoader) || (modernLoader instanceof IdReadLoader)) {
            return results.get(0);
        } else {
            return results;
        }

    }

    public static class CallbackHandler<T> implements Runnable {

        private final LoaderAdapter<T> adapter;
        private final AbstractPipeLoader<T> modernLoader;
        private final Object data;

        public CallbackHandler(LoaderAdapter<T> adapter, AbstractPipeLoader loader, Object data) {
            super();
            this.adapter = adapter;
            this.modernLoader = loader;
            this.data = data;
        }

        @Override
        public void run() {
            if (modernLoader.hasException()) {
                final Exception exception = modernLoader.getException();
                Log.e(TAG, exception.getMessage(), exception);
                if (modernLoader.getCallback() instanceof AbstractFragmentCallback) {
                    adapter.fragmentFailure(modernLoader.getCallback(), exception);
                } else if (modernLoader.getCallback() instanceof AbstractSupportFragmentCallback) {
                    adapter.supportFragmentFailure(modernLoader.getCallback(), exception);
                } else if (modernLoader.getCallback() instanceof AbstractActivityCallback) {
                    adapter.activityFailure(modernLoader.getCallback(), exception);
                } else {
                    modernLoader.getCallback().onFailure(exception);
                }

            } else {

                if (modernLoader.getCallback() instanceof AbstractFragmentCallback) {
                    adapter.fragmentSuccess(modernLoader.getCallback(), data);
                } else if (modernLoader.getCallback() instanceof AbstractSupportFragmentCallback) {
                    adapter.supportFragmentSuccess(modernLoader.getCallback(), data);
                } else if (modernLoader.getCallback() instanceof AbstractActivityCallback) {
                    adapter.activitySuccess(modernLoader.getCallback(), data);
                } else {
                    modernLoader.getCallback().onSuccess((T) data);
                }
            }

        }
    }

    private void verifyCallback(Callback<List<T>> callback) {
        if (callback instanceof AbstractActivityCallback) {
            if (activity == null) {
                throw new IllegalStateException(
                        "An AbstractActivityCallback was supplied, but there is no Activity.");
            }
        } else if (callback instanceof AbstractFragmentCallback) {
            if (fragment == null) {
                throw new IllegalStateException(
                        "An AbstractFragmentCallback was supplied, but there is no Fragment.");
            }
        } else if (callback instanceof AbstractSupportFragmentCallback) {
            if (supportFragment == null) {
                throw new IllegalStateException(
                        "An AbstractSupportFragmentCallback was supplied, but there is no Fragment.");
            }
        }
    }

    private synchronized void addId(String name, int id) {
        List<Integer> ids = this.idsForNamedPipes.get(name);
        if (ids == null) {
            this.idsForNamedPipes.put(name, (ids = new ArrayList<Integer>()));
        }
        ids.add(id);
    }

}