1 /* 2 * Copyright the original author or authors. 3 * 4 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.mozilla.org/MPL/MPL-1.1.html 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import org.as2lib.util.ArrayUtil; 18 import org.as2lib.env.reflect.ReflectUtil; 19 import org.as2lib.data.holder.Map; 20 import org.as2lib.data.holder.map.HashMap; 21 import org.as2lib.env.except.IllegalArgumentException; 22 import org.as2lib.env.event.distributor.CompositeEventDistributorControl; 23 import org.as2lib.env.event.distributor.EventDistributorControl; 24 import org.as2lib.env.event.distributor.EventDistributorControlFactory; 25 26 /** 27 * {@code AbstractCompositeEventDistributorControl} is the default implementation 28 * of the {@code CompositeEventDistributorControl} interface. 29 * 30 * <p>To use its functionalities, simply extend it and set a factory for the default 31 * event distributors. 32 * 33 * @author Martin Heidegger 34 */ 35 class org.as2lib.env.event.distributor.AbstractCompositeEventDistributorControl implements CompositeEventDistributorControl { 36 37 /* Factory to create default event distributors for the different types */ 38 private var eventDistributorControlFactory:EventDistributorControlFactory; 39 40 /* All added listeners. */ 41 private var listeners:Array; 42 43 /* All available {@code EventDistributorControl} instances mapped to classes and interfaces. */ 44 private var distributorMap:Map; 45 46 /** 47 * Creates a new {@code AbstractCompositeEventDistributorControl} instance. 48 * 49 * @param eventDistributorControlFactory the factory to create event distributor 50 * controls for the different listener types 51 */ 52 public function AbstractCompositeEventDistributorControl(eventDistributorControlFactory:EventDistributorControlFactory) { 53 this.eventDistributorControlFactory = eventDistributorControlFactory; 54 distributorMap = new HashMap(); 55 listeners = new Array(); 56 } 57 58 /** 59 * Adds a the given listener to this event distributor control. 60 * 61 * <p>It validates if the passed-in {@code listener} is of any of the accepted 62 * listener types. 63 * 64 * <p>The listener will be added to all matching distributors. 65 * 66 * @param l the Listener to add to the control 67 * @throws IllegalArgumentException if the given listener does not match any of the 68 * accepted types. 69 */ 70 public function addListener(l):Void { 71 if (!hasListener(l)) { 72 var acceptedTypes:Array = distributorMap.getKeys(); 73 var existingDistributors:Array = distributorMap.getValues(); 74 var added:Boolean = false; 75 for (var i:Number = 0; i < acceptedTypes.length; i++) { 76 if (l instanceof acceptedTypes[i]) { 77 existingDistributors[i].addListener(l); 78 added = true; 79 } 80 } 81 if (added) { 82 listeners.push(l); 83 } else { 84 var message:String = "The passed-in listener [" + ReflectUtil.getTypeNameForInstance(l) + "] does not match any of the accepted listener types."; 85 var size:Number = distributorMap.size(); 86 if (size > 0) { 87 message += "(" + size + "):"; 88 for (var i:Number = 0; i < size; i++) { 89 message += "\n - " + ReflectUtil.getTypeNameForType(acceptedTypes[i]); 90 } 91 } else { 92 message += "(No types accepted)."; 93 } 94 throw new IllegalArgumentException(message, this, arguments); 95 } 96 } 97 } 98 99 /** 100 * Removes the given listener from this distributor control and thus from listening 101 * to events. 102 * 103 * @param l the listener to remove 104 */ 105 public function removeListener(l):Void { 106 if (hasListener(l)) { 107 var acceptedTypes:Array = distributorMap.getKeys(); 108 var existingDistributors:Array = distributorMap.getValues(); 109 for (var i:Number = 0; i < acceptedTypes.length; i++) { 110 if (l instanceof acceptedTypes[i]) { 111 existingDistributors[i].removeListener(l); 112 } 113 } 114 ArrayUtil.removeElement(listeners, l); 115 } 116 } 117 118 /** 119 * Adds a list of listeners to this event distributor control to listen to events. 120 * 121 * @param listeners the list of listeners to add 122 * @throws IllegalArgumentException if any listener is not accepted (the listeners 123 * before the certain listener will be added) 124 */ 125 public function addAllListeners(listeners:Array):Void { 126 for (var i:Number = 0; i < listeners.length; i++) { 127 addListener(listeners[i]); 128 } 129 } 130 131 /** 132 * Removes all added listeners. 133 */ 134 public function removeAllListeners(Void):Void { 135 var list:Array = getAllListeners(); 136 for (var i:Number = 0; i < list.length; i++) { 137 removeListener(list[i]); 138 } 139 } 140 141 /** 142 * Returns a list that contains all listeners. 143 * 144 * @return the list that contains all listeners 145 */ 146 public function getAllListeners(Void):Array { 147 return listeners.concat(); 148 } 149 150 /** 151 * Checks if the given listener has already been added. 152 * 153 * @param l the listener to check whether it has already been added 154 * @return {@code true} if the listener has been added 155 */ 156 public function hasListener(l):Boolean { 157 return ArrayUtil.contains(listeners, l); 158 } 159 160 /** 161 * Adds acception for the given {@code listenerType}. 162 * 163 * <p>{@code addListener} does not allow listeners that do not match (instanceof) 164 * at least one of the accepted listener types. 165 * 166 * @param type the type of listeners that are accepted 167 * @see #registerEventDistributorControl 168 * @see #registerDefaultEventDistributorControl 169 */ 170 public function acceptListenerType(listenerType:Function):Void { 171 if (!distributorMap.containsKey(listenerType)) { 172 var distributor:EventDistributorControl = eventDistributorControlFactory.createEventDistributorControl(listenerType); 173 for (var i:Number = 0; i < listeners.length; i++) { 174 if (listeners[i] instanceof listenerType) { 175 distributor.addListener(listeners[i]); 176 } 177 } 178 distributorMap.put(listenerType, distributor); 179 } 180 } 181 182 /** 183 * Returns the distributor that can be used to broadcast an event to all added 184 * listeners that match the distributor's type. 185 * 186 * <p>If the given {@code type} has not been accepted as listener type yet, it will 187 * be accepted after you invoked this method. 188 * 189 * <p>Note that the returned distributor will not be updated if you add a new 190 * distributor control for the given {@code type} after you obtained a distributor. 191 * You must get a new distributor if you want an updated one. 192 * 193 * @return the distributor to distribute events 194 */ 195 public function getDistributor(type:Function) { 196 if (!distributorMap.containsKey(type)) { 197 acceptListenerType(type); 198 } 199 var distributor:EventDistributorControl = distributorMap.get(type); 200 return distributor.getDistributor(); 201 } 202 203 /** 204 * Registers the given {@code eventDistributorControl} with its listener and 205 * distributor type returned by its {@link EventDistributorControl#getType} method. 206 * 207 * <p>The type is then automatically an accepted listener type. 208 * 209 * <p>If there is already a distributor control registered for the given type, it 210 * will be overwritten. 211 * 212 * <p>If you hold references to distributors of this type, returned by the 213 * {@link #getDistributor} method, you will have to update them, else events will 214 * not be distributed to newly registered listeners of that type. 215 * 216 * <p>You use this method if you have a specific event that should be executed with 217 * a special kind of distributor, for example with a consumable one. 218 * 219 * @param eventDistributorControl the event distributor control to use for event 220 * distribution for the given type 221 * @throws IllegalArgumentException if the given argument {@code eventDistributorControl} 222 * is {@code null} or {@code undefined} 223 * @see #registerDefaultEventDistributorControl 224 * @see #acceptListenerType 225 */ 226 public function registerEventDistributorControl(eventDistributorControl:EventDistributorControl):Void { 227 if (!eventDistributorControl) throw new IllegalArgumentException("Argument 'eventDistributorControl' [" + eventDistributorControl + "] must neither be 'null' nor 'undefined'.", this, arguments); 228 var type:Function = eventDistributorControl.getType(); 229 eventDistributorControl.removeAllListeners(); 230 for (var i:Number = 0; i < listeners.length; i++) { 231 if (listeners[i] instanceof type) { 232 eventDistributorControl.addListener(listeners[i]); 233 } 234 } 235 distributorMap.put(type, eventDistributorControl); 236 } 237 238 /** 239 * Registers a default event distributor control with the given listener and 240 * distributor {@code type}. 241 * 242 * <p>The {@code type} is then automatically an accepted listener type. 243 * 244 * <p>If there is already a distributor control registered for the given type, it 245 * will be overwritten. 246 * 247 * @param type the type to register a default distributor control with 248 * @throws IllegalArgumentException if argument {@code type} is {@code null} or 249 * {@code undefined} 250 * @see #acceptListenerType 251 */ 252 public function registerDefaultEventDistributorControl(type:Function):Void { 253 if (!type) throw new IllegalArgumentException("Argument 'type' [" + type + "] must neither be 'null' nor 'undefined'.", this, arguments); 254 registerEventDistributorControl(eventDistributorControlFactory.createEventDistributorControl(type)); 255 } 256 257 }