/* * Copyright 2007-2013 * Licensed under GNU Lesser General Public License * * This file is part of EpochX * * EpochX is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EpochX is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with EpochX. If not, see <http://www.gnu.org/licenses/>. * * The latest version is available from: http://www.epochx.org */ package org.epochx.event.stat; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.epochx.event.Event; import org.epochx.event.EventManager; import org.epochx.event.Listener; /** * The <code>AbstractStat</code> represent the base class for classes that * gathers data and statistics about events. It also works as a central * repository for registering, removing and retrieving stat objects. * * @see Event */ public abstract class AbstractStat<T extends Event> { /** * An empty list of dependencies. */ public static final List<Class<? extends AbstractStat<?>>> NO_DEPENDENCIES = new ArrayList<Class<? extends AbstractStat<?>>>( 0); /** * The central repository of <code>AbstractStat</code> objects. */ private static final HashMap<Class<?>, Object> REPOSITORY = new HashMap<Class<?>, Object>(); /** * This is the stat listener. When the stat is registered, its listener is added to * the {@link EventManager}. */ private Listener<T> listener = new Listener<T>() { @Override public void onEvent(T event) { AbstractStat.this.refresh(event); } }; /** * The event that trigger the stat to clear its values. */ private Class<Event> clearOnEvent; /** * This is the clear listener. This is only created if the event to trigger the * {@link #clear()} method is specified. */ private Listener<Event> clearOnListener; /** * Constructs an <code>AbstractStat</code>. */ public AbstractStat() { this(NO_DEPENDENCIES); } /** * Constructs an <code>AbstractStat</code>. * * @param dependency the dependency of this stat. */ @SuppressWarnings("unchecked") public AbstractStat(Class<? extends AbstractStat<?>> dependency) { this(Arrays.<Class<? extends AbstractStat<?>>> asList(dependency)); } /** * Constructs an <code>AbstractStat</code>. The array of dependencies can be * empty, in case this stat has no dependencies. * * @param dependencies the array of dependencies of this stat. */ public AbstractStat(Class<? extends AbstractStat<?>> ... dependencies) { this(Arrays.asList(dependencies)); } /** * Constructs an <code>AbstractStat</code>. The list of dependencies can be * empty, in case this stat has no dependencies. * * @param dependencies the list of dependencies of this stat. */ public AbstractStat(List<Class<? extends AbstractStat<?>>> dependencies) { for (Class<? extends AbstractStat<?>> dependency: dependencies) { AbstractStat.register(dependency); } } /** * Constructs an <code>AbstractStat</code>. * * @param clearOn the event that trigger the stat to clear its values. * @param dependency the dependency of this stat. */ @SuppressWarnings("unchecked") public <E extends Event> AbstractStat(Class<E> clearOn, Class<? extends AbstractStat<?>> dependency) { this(clearOn, Arrays.<Class<? extends AbstractStat<?>>> asList(dependency)); } /** * Constructs an <code>AbstractStat</code>. The array of dependencies can be * empty, in case this stat has no dependencies. * * @param clearOn the event that trigger the stat to clear its values. * @param dependencies the array of dependencies of this stat. */ public <E extends Event> AbstractStat(Class<E> clearOn, Class<? extends AbstractStat<?>> ... dependencies) { this(clearOn, Arrays.asList(dependencies)); } /** * Constructs an <code>AbstractStat</code>. The list of dependencies can be * empty, in case this stat has no dependencies. * * @param clearOn the event that trigger the stat to clear its values. * @param dependencies the list of dependencies of this stat. */ @SuppressWarnings("unchecked") public <E extends Event> AbstractStat(Class<E> clearOn, List<Class<? extends AbstractStat<?>>> dependencies) { for (Class<? extends AbstractStat<?>> dependency: dependencies) { AbstractStat.register(dependency); } Listener<E> trigger = new Listener<E>() { @Override public void onEvent(E event) { AbstractStat.this.clear(); }; }; EventManager.getInstance().add(clearOn, trigger); clearOnEvent = (Class<Event>) clearOn; clearOnListener = (Listener<Event>) trigger; } /** * Returns the class of the generic type T. */ @SuppressWarnings("unchecked") private Class<T> getEvent() { return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } /** * Gathers the information about the event. * * @param event the event */ public abstract void refresh(T event); /** * Clears the cached values. This method is automatically called when a clear on event is specified. */ public void clear() { } /** * Registers the specified <code>AbstractStat</code> in the repository, if * it is not already registered. * * @param type the class of <code>AbstractStat</code> to be registered. */ @SuppressWarnings("unchecked") public static <E extends Event, V extends AbstractStat<?>> void register(Class<V> type) { // if the repository already contains an instance of the specified stat, // we do not create a new one; otherwise, we create a new instance and // register its listener in the EventManager if (!REPOSITORY.containsKey(type)) { try { AbstractStat<E> stat = (AbstractStat<E>) type.newInstance(); REPOSITORY.put(type, stat); EventManager.getInstance().add(stat.getEvent(), stat.listener); } catch (Exception e) { throw new RuntimeException("Could not create an instance of " + type, e); } } } /** * Removes the specified <code>AbstractStat</code> from the repository. * * @param type the class of <code>AbstractStat</code> to be removed. */ public static <E extends Event> void remove(Class<? extends AbstractStat<E>> type) { if (REPOSITORY.containsKey(type)) { AbstractStat<E> stat = type.cast(REPOSITORY.remove(type)); EventManager.getInstance().remove(stat.getEvent(), stat.listener); EventManager.getInstance().remove(stat.clearOnEvent, stat.clearOnListener); } } /** * Returns the <code>AbstractStat</code> object of the specified class. If * the <code>AbstractStat</code> has been registered, it returns * <code>null</code>. * * @return the <code>AbstractStat</code> object of the specified class; * <code>null</code> if the <code>AbstractStat</code> has not been * registered. */ public static <V extends AbstractStat<?>> V get(Class<V> type) { return type.cast(REPOSITORY.get(type)); } /** * Removes all registered <code>AbstractStat</code> objects from the * repository. */ @SuppressWarnings("unchecked") public static <E extends Event> void reset() { List<Class<?>> registered = new ArrayList<Class<?>>(REPOSITORY.keySet()); for (Class<?> type: registered) { AbstractStat.remove((Class<? extends AbstractStat<E>>) type); } } }