de.halfbit.featured.FeatureHost.java Source code

Java tutorial

Introduction

Here is the source code for de.halfbit.featured.FeatureHost.java

Source

/*
 * Copyright (C) 2016 Sergej Shafarenka, www.halfbit.de
 *
 * 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 de.halfbit.featured;

import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;

/**
 * Base class for generated feature host classes.
 *
 * @author sergej shafarenka
 */
public abstract class FeatureHost<FH extends FeatureHost, C> {

    /**
     * Callback interface which is called right after a feature event has been dispatched.
     */
    public interface OnDispatchCompleted {

        /**
         * Called after a feature event has just been dispatched.
         */
        void onDispatchCompleted();
    }

    protected static abstract class Event {
        @Nullable
        Event mNextEvent;

        @Nullable
        protected OnDispatchCompleted mOnDispatchCompleted;

        protected abstract void dispatch(@NonNull Feature feature);
    }

    private final C mContext;
    private final ArrayMap<String, Feature> mFeatures;
    private Event mDispatchingEvent;

    /**
     * Creates new feature host instance and attaches given context to it.
     *
     * @param context context to be attached
     */
    public FeatureHost(@NonNull C context) {
        mFeatures = new ArrayMap<>(10);
        mContext = context;
    }

    /**
     * Returns context attached to this feature host instance.
     *
     * @return context attached to this feature host instance.
     */
    @NonNull
    protected C getContext() {
        return mContext;
    }

    /**
     * Registers a feature at the feature host.
     *
     * @param feature feature instance to be registered
     */
    @SuppressWarnings("unchecked")
    protected void addFeature(Feature feature, @Nullable String featureName) {
        if (featureName == null) {
            featureName = feature.getClass().toString();
        }

        Feature registeredFeature = mFeatures.put(featureName, feature);
        if (registeredFeature != null) {
            throw new IllegalArgumentException(
                    String.format("There is already a feature %s registered with name %s. "
                            + "Use different feature name if you want to register same feature "
                            + "class multiple times.", registeredFeature, featureName));
        }

        feature.attachFeatureHost(this);
    }

    /**
     * Get a feature by its classname, which is registered in the feature host.
     *
     * @param featureClass feature class
     * @return registered feature of {@code null}
     */
    @Nullable
    public <F extends Feature> F getFeature(@NonNull Class<F> featureClass) {
        //noinspection unchecked
        return (F) mFeatures.get(featureClass.toString());
    }

    @Nullable
    public <F extends Feature> F getFeature(@NonNull Class<F> featureClass, @NonNull String featureName) {
        //noinspection unchecked
        return (F) mFeatures.get(featureName);
    }

    protected void dispatch(Event event) {
        assertMainThread();

        if (mDispatchingEvent == null) {
            mDispatchingEvent = event;

            // dispatch event right away
            Event e = mDispatchingEvent;
            while (e != null) {

                // dispatch to all features first
                for (int i = 0, size = mFeatures.size(); i < size; i++) {
                    //noinspection unchecked
                    e.dispatch(mFeatures.valueAt(i));
                }

                // dispatch event completion now
                if (e.mOnDispatchCompleted != null) {
                    e.mOnDispatchCompleted.onDispatchCompleted();
                }

                // pick up next event
                e = e.mNextEvent;
            }

            mDispatchingEvent = null;
            return;
        }

        // already dispatching, put event to the end of chain
        Event e = mDispatchingEvent;
        while (e.mNextEvent != null) {
            e = e.mNextEvent;
        }
        e.mNextEvent = event;
    }

    private void assertMainThread() {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            throw new IllegalStateException(
                    "Dispatch must be called in MainThread. Current thread: " + Thread.currentThread());
        }
    }

}