package com.vitco.manager.action; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; /** * Basic functionality for actions */ public abstract class ActionManagerPrototype<T> implements ActionManagerInterface<T> { // maps strings to action private final Map<String, T> map = new HashMap<String, T>(); // maps action names to pending actions (stack) private final Map<String, ArrayList<Runnable>> actionQueStack = new HashMap<String, ArrayList<Runnable>>(); // maps actions to keys // Note: usually this should be a one to one mapping (unless an action is mapped to different keys) private final HashMap<T, HashSet<String>> actionToKeys = new HashMap<T, HashSet<String>>(); @Override public final void performWhenActionIsReady(String action, Runnable thread) { if (map.containsKey(action)) { thread.run(); // the action is already ready } else { // we need to wait till the action is ready ArrayList<Runnable> value; if (actionQueStack.containsKey(action)) { value = actionQueStack.get(action); } else { value = new ArrayList<Runnable>(); } value.add(thread); actionQueStack.put(action, value); } } // allows to register an action @Override public final void registerAction(String key, T action) { if (map.containsKey(key)) { System.err.println("Error: The action \"" + key + "\" (" + getClassName() + ") is already registered!"); } else { map.put(key, action); // register the key handle for this action HashSet<String> actionKeyHandles = actionToKeys.get(action); if (actionKeyHandles == null) { actionKeyHandles = new HashSet<String>(); actionToKeys.put(action, actionKeyHandles); } actionKeyHandles.add(key); // run the Runnables that were waiting for the actions if (actionQueStack.containsKey(key)) { ArrayList<Runnable> value = actionQueStack.get(key); for (Runnable thread : value) { // note: this can create an error if the action is not // the extended type of AbstractAction that was // expected (e.g. dummy action) thread.run(); } actionQueStack.remove(key); // free the key } } } // allows to retrieve an action for a key @Override public final T getAction(String key) { if (map.containsKey(key)) { return map.get(key); } else { System.err.println("Error: The action \"" + key + "\" (" + getClassName() + ") is not registered!"); return null; } } // retrieve keys for an action @Override public final String[] getActionKeys(T action) { HashSet<String> actionKeyHandles = actionToKeys.get(action); if (actionKeyHandles != null) { String[] result = new String[actionKeyHandles.size()]; actionKeyHandles.toArray(result); return result; } else { return new String[0]; } } // =========================== // BELOW ERROR CHECKING protected abstract T getDummyAction(String actionName); protected abstract String getClassName(); // holds action names, to detect possible errors private final ArrayList<String> actionNames = new ArrayList<String>(); // validate things @Override public final boolean performValidityCheck() { boolean result = true; for (final String actionName : actionNames) { if (!map.containsKey(actionName)) { System.err.println("Error: The action \"" + actionName + "\" (" + getClassName() + ") is not registered!"); System.err.println("Creating dummy action."); // register dummy action registerAction(actionName, getDummyAction(actionName)); result = false; } } for (String key : map.keySet()) { if (!actionNames.contains(key)) { System.err.println("Error: The action \"" + key + "\" (" + getClassName() + ") is never used!"); } } // this should always be empty as a dummy action is registered above // (unless the action is never marked as used) for (String key : actionQueStack.keySet()) { System.err.println("Error: The action \"" + key + "\" (" + getClassName() + ") was never registered!"); System.err.println("Did you forget to register that the action is used?!"); } return result; } @Override public final void registerActionIsUsed(String key) { // only need to register this action name one (several pieces of code // might be executing this action!) if (!actionNames.contains(key)) { actionNames.add(key); } } }