/* Copyright 2012 Jan Ove Saltvedt This file is part of KBot. KBot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. KBot 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 General Public License for more details. You should have received a copy of the GNU General Public License along with KBot. If not, see <http://www.gnu.org/licenses/>. */ package com.kbotpro.bot; import com.kbotpro.scriptsystem.events.KPaintEventListener; import com.kbotpro.scriptsystem.events.PaintEventListener; import com.kbotpro.scriptsystem.events.RandomListener; import com.kbotpro.scriptsystem.events.ServerMessageListener; import com.kbotpro.scriptsystem.graphics.KGraphics; import java.awt.*; import java.util.EventListener; import java.io.ObjectOutputStream; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Array; /** * More or less a copy of AWTEventMulticaster with some extra event types */ public class BotEventMulticaster implements PaintEventListener, KPaintEventListener, ServerMessageListener, RandomListener { protected final EventListener a, b; /** * Creates an event multicaster instance which chains listener-a * with listener-b. Input parameters <code>a</code> and <code>b</code> * should not be <code>null</code>, though implementations may vary in * choosing whether or not to throw <code>NullPointerException</code> * in that case. * @param a listener-a * @param b listener-b */ protected BotEventMulticaster(EventListener a, EventListener b) { this.a = a; this.b = b; } /** * Removes a listener from this multicaster. * <p> * The returned multicaster contains all the listeners in this * multicaster with the exception of all occurrences of {@code oldl}. * If the resulting multicaster contains only one regular listener * the regular listener may be returned. If the resulting multicaster * is empty, then {@code null} may be returned instead. * <p> * No exception is thrown if {@code oldl} is {@code null}. * * @param oldl the listener to be removed * @return resulting listener */ protected EventListener remove(EventListener oldl) { if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal(a, oldl); EventListener b2 = removeInternal(b, oldl); if (a2 == a && b2 == b) { return this; // it's not here } return addInternal(a2, b2); } /** * Returns the resulting multicast listener from adding listener-a * and listener-b together. * If listener-a is null, it returns listener-b; * If listener-b is null, it returns listener-a * If neither are null, then it creates and returns * a new BotEventMulticaster instance which chains a with b. * @param a event listener-a * @param b event listener-b */ protected static EventListener addInternal(EventListener a, EventListener b) { if (a == null) return b; if (b == null) return a; return new BotEventMulticaster(a, b); } /** * Returns the resulting multicast listener after removing the * old listener from listener-l. * If listener-l equals the old listener OR listener-l is null, * returns null. * Else if listener-l is an instance of BotEventMulticaster, * then it removes the old listener from it. * Else, returns listener l. * @param l the listener being removed from * @param oldl the listener being removed */ protected static EventListener removeInternal(EventListener l, EventListener oldl) { if (l == oldl || l == null) { return null; } else if (l instanceof BotEventMulticaster) { return ((BotEventMulticaster)l).remove(oldl); } else { return l; // it's not here } } /* Serialization support. */ protected void saveInternal(ObjectOutputStream s, String k) throws IOException { if (a instanceof BotEventMulticaster) { ((BotEventMulticaster)a).saveInternal(s, k); } else if (a instanceof Serializable) { s.writeObject(k); s.writeObject(a); } if (b instanceof BotEventMulticaster) { ((BotEventMulticaster)b).saveInternal(s, k); } else if (b instanceof Serializable) { s.writeObject(k); s.writeObject(b); } } protected static void save(ObjectOutputStream s, String k, EventListener l) throws IOException { if (l == null) { return; } else if (l instanceof BotEventMulticaster) { ((BotEventMulticaster)l).saveInternal(s, k); } else if (l instanceof Serializable) { s.writeObject(k); s.writeObject(l); } } /* * Recursive method which returns a count of the number of listeners in * EventListener, handling the (common) case of l actually being an * BotEventMulticaster. Additionally, only listeners of type listenerType * are counted. Method modified to fix bug 4513402. -bchristi */ private static int getListenerCount(EventListener l, Class listenerType) { if (l instanceof BotEventMulticaster) { BotEventMulticaster mc = (BotEventMulticaster)l; return getListenerCount(mc.a, listenerType) + getListenerCount(mc.b, listenerType); } else { // Only count listeners of correct type return listenerType.isInstance(l) ? 1 : 0; } } /* * Recusive method which populates EventListener array a with EventListeners * from l. l is usually an BotEventMulticaster. Bug 4513402 revealed that * if l differed in type from the element type of a, an ArrayStoreException * would occur. Now l is only inserted into a if it's of the appropriate * type. -bchristi */ private static int populateListenerArray(EventListener[] a, EventListener l, int index) { if (l instanceof BotEventMulticaster) { BotEventMulticaster mc = (BotEventMulticaster)l; int lhs = populateListenerArray(a, mc.a, index); return populateListenerArray(a, mc.b, lhs); } else if (a.getClass().getComponentType().isInstance(l)) { a[index] = l; return index + 1; } // Skip nulls, instances of wrong class else { return index; } } /** * Returns an array of all the objects chained as * <code><em>Foo</em>Listener</code>s by the specified * <code>java.util.EventListener</code>. * <code><em>Foo</em>Listener</code>s are chained by the * <code>BotEventMulticaster</code> using the * <code>add<em>Foo</em>Listener</code> method. * If a <code>null</code> listener is specified, this method returns an * empty array. If the specified listener is not an instance of * <code>BotEventMulticaster</code>, this method returns an array which * contains only the specified listener. If no such listeners are chanined, * this method returns an empty array. * * @param l the specified <code>java.util.EventListener</code> * @param listenerType the type of listeners requested; this parameter * should specify an interface that descends from * <code>java.util.EventListener</code> * @return an array of all objects chained as * <code><em>Foo</em>Listener</code>s by the specified multicast * listener, or an empty array if no such listeners have been * chained by the specified multicast listener * @exception NullPointerException if the specified * {@code listenertype} parameter is {@code null} * @exception ClassCastException if <code>listenerType</code> * doesn't specify a class or interface that implements * <code>java.util.EventListener</code> * * @since 1.4 */ public static <T extends EventListener> T[] getListeners(EventListener l, Class<T> listenerType) { if (listenerType == null) { throw new NullPointerException ("Listener type should not be null"); } int n = getListenerCount(l, listenerType); T[] result = (T[]) Array.newInstance(listenerType, n); populateListenerArray(result, l, 0); return result; } public void onRepaint(Graphics g) { ((PaintEventListener)a).onRepaint(g); ((PaintEventListener)b).onRepaint(g); } public static PaintEventListener add(PaintEventListener a, PaintEventListener b) { return (PaintEventListener)addInternal(a, b); } public static PaintEventListener remove(PaintEventListener l, PaintEventListener oldl) { return (PaintEventListener) removeInternal(l, oldl); } public static KPaintEventListener add(KPaintEventListener a, KPaintEventListener b) { return (KPaintEventListener)addInternal(a, b); } public static KPaintEventListener remove(KPaintEventListener l, KPaintEventListener oldl) { return (KPaintEventListener) removeInternal(l, oldl); } public static ServerMessageListener add(ServerMessageListener a, ServerMessageListener b) { return (ServerMessageListener)addInternal(a, b); } public static ServerMessageListener remove(ServerMessageListener l, ServerMessageListener oldl) { return (ServerMessageListener) removeInternal(l, oldl); } public void onRepaint(KGraphics g) { ((KPaintEventListener)a).onRepaint(g); ((KPaintEventListener)b).onRepaint(g); } /** * Is called when the client recieves a server message. * * @param message */ public void onServerMessage(String message) { ((ServerMessageListener)a).onServerMessage(message); ((ServerMessageListener)b).onServerMessage(message); } public static RandomListener remove(RandomListener l, RandomListener oldl) { return (RandomListener) removeInternal(l, oldl); } public static RandomListener add(RandomListener a, RandomListener b) { return (RandomListener)addInternal(a, b); } /** * Is called when a random event activates. * * @param name The name of the random * @return boolean, true if you want to allow this random to run, false if you want to make it not run this time. */ public boolean randomActivated(String name) { return ((RandomListener) a).randomActivated(name) && ((RandomListener) b).randomActivated(name); } }