package org.holoeverywhere.addon;
import org.holoeverywhere.addon.IAddon.Addon;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public final class IAddonBasicAttacher<V extends IAddonBase<Z>, Z> implements IAddonAttacher<V> {
private final Map<Class<? extends IAddon>, V> mAddons = new HashMap<Class<? extends IAddon>, V>();
private final Set<V> mAddonsSet = new TreeSet<V>(new AddonComparator());
private List<V> mAddonsList;
private boolean mLockAttaching = false;
private Z mObject;
public IAddonBasicAttacher(Z object) {
mObject = object;
}
public static void attachAnnotations(IAddonAttacher<?> attacher) {
if (attacher.getClass().isAnnotationPresent(Addons.class)) {
for (Class<? extends IAddon> clazz : attacher.getClass().getAnnotation(Addons.class).value()) {
attacher.addon(clazz);
}
}
}
@Override
public <T extends V> T addon(Class<? extends IAddon> clazz) {
return addon(clazz, true);
}
@SuppressWarnings("unchecked")
private <T extends V> T addon(Class<? extends IAddon> clazz, boolean checkConflicts) {
T addon = (T) mAddons.get(clazz);
if (addon == null) {
if (mLockAttaching) {
throw AttachException.afterInit(mObject, clazz);
}
addon = IAddon.obtain(clazz, mObject);
if (addon == null) {
return null;
}
mAddons.put(clazz, addon);
mAddonsSet.add(addon);
mAddonsList = null;
if (checkConflicts) {
checkConflicts();
}
}
return addon;
}
@Override
public void addon(Collection<Class<? extends IAddon>> classes) {
if (classes == null || classes.size() == 0) {
return;
}
for (Class<? extends IAddon> clazz : classes) {
addon(clazz, false);
}
checkConflicts();
}
@Override
public <T extends V> T addon(String classname) {
return addon(IAddon.makeAddonClass(classname));
}
private void checkConflicts() {
Set<String> attachedAddons = new HashSet<String>();
Map<String, String> conflictAddons = new HashMap<String, String>();
for (V addon : mAddonsSet) {
Class<? extends IAddon> clazz = addon.getParent().getClass();
final String clazzName = clazz.getName();
attachedAddons.add(clazzName);
if (!clazz.isAnnotationPresent(Addon.class)) {
continue;
}
Addon addonMeta = clazz.getAnnotation(Addon.class);
for (String a : addonMeta.conflictStrings()) {
conflictAddons.put(a, clazzName);
}
for (Class<? extends IAddon> a : addonMeta.conflict()) {
conflictAddons.put(a.getName(), clazzName);
}
}
StringBuilder builder = null;
for (String addon : conflictAddons.keySet()) {
if (attachedAddons.contains(addon)) {
if (builder == null) {
builder = new StringBuilder();
} else {
builder.append('\n');
}
builder.append(String.format(
"Found addon conflict: %s is cannot be attached together with %s",
addon, conflictAddons.get(addon)));
}
}
if (builder != null) {
throw AttachException.conflict(builder.toString());
}
}
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 == null ? null : 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 (mAddonsSet.size() == 0) {
return callback.post();
}
if (mAddonsList == null) {
mAddonsList = new ArrayList<V>(mAddonsSet);
}
final int addonCount = mAddonsList.size();
for (int i = 0; i < addonCount; i++) {
if (callback.action(mAddonsList.get(i))) {
return true;
}
}
return callback.post();
}
public void reset() {
mAddons.clear();
mAddonsSet.clear();
mAddonsList = null;
mLockAttaching = false;
}
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;
}
}
}