
package org.holoeverywhere.addon;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.holoeverywhere.addon.IAddon.Addon;

public final class IAddonBasicAttacher<V extends IAddonBase<Z>, Z> implements IAddonAttacher<V> {
    private final class AddonComparator implements Comparator<V> {
        @Override
        public int compare(V lhs, V rhs) {
            final int i1 = getWeight(lhs.getParent());
            final int i2 = getWeight(rhs.getParent());
            return i1 > i2 ? 1 : i1 < i2 ? -1 : 0;
        }

        private int getWeight(IAddon addon) {
            if (addon.getClass().isAnnotationPresent(Addon.class)) {
                return addon.getClass().getAnnotation(Addon.class).weight();
            }
            return -1;
        }
    }

    private final Map<Class<? extends IAddon>, V> mAddons = new HashMap<Class<? extends IAddon>, V>();
    private final Set<V> mAddonsList = new TreeSet<V>(new AddonComparator());
    private boolean mLockAttaching = false;
    private Z mObject;

    public IAddonBasicAttacher(Z object) {
        mObject = object;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T extends V> T addon(Class<? extends IAddon> clazz) {
        T addon = (T) mAddons.get(clazz);
        if (addon == null) {
            if (mLockAttaching) {
                throw new AttachException(mObject, clazz);
            }
            addon = IAddon.obtain(clazz, mObject);
            if (addon == null) {
                return null;
            }
            mAddons.put(clazz, addon);
            mAddonsList.add(addon);
        }
        return addon;
    }

    @Override
    public void addon(Collection<Class<? extends IAddon>> classes) {
        if (classes == null) {
            return;
        }
        for (Class<? extends IAddon> clazz : classes) {
            addon(clazz);
        }
    }

    @Override
    public <T extends V> T addon(String classname) {
        return addon(IAddon.makeAddonClass(classname));
    }

    public void inhert(Collection<Class<? extends IAddon>> sourceClasses) {
        if (sourceClasses == null || sourceClasses.size() == 0) {
            return;
        }
        List<Class<? extends IAddon>> classes = new ArrayList<Class<? extends IAddon>>();
        for (Class<? extends IAddon> clazz : sourceClasses) {
            if (!clazz.isAnnotationPresent(Addon.class)) {
                continue;
            }
            Addon addon = clazz.getAnnotation(Addon.class);
            if (addon.inhert()) {
                classes.add(clazz);
            }
        }
        addon(classes);
    }

    public void inhert(IAddonAttacher<?> attacher) {
        inhert(attacher.obtainAddonsList());
    }

    @Override
    public boolean isAddonAttached(Class<? extends IAddon> clazz) {
        return mAddons.containsKey(clazz);
    }

    @Override
    public void lockAttaching() {
        mLockAttaching = true;
    }

    @Override
    public Collection<Class<? extends IAddon>> obtainAddonsList() {
        return new ArrayList<Class<? extends IAddon>>(mAddons.keySet());
    }

    @Override
    public boolean performAddonAction(AddonCallback<V> callback) {
        if (mAddons.size() == 0) {
            return false;
        }
        callback.pre();
        boolean result = false;
        for (V addon : mAddonsList) {
            result = callback.performAction(addon);
            if (callback.mStopped) {
                return result;
            }
        }
        return callback.post();
    }

    public void reset() {
        mAddons.clear();
        mAddonsList.clear();
        mLockAttaching = false;
    }
}

@@ -2842,16 +2842,42 @@
 attacher
+ == null ? null : attacher
 .obtainA

Fix #448 [deploy snapshot]