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.core.BasicClass; 18 import org.as2lib.env.except.IllegalArgumentException; 19 import org.as2lib.env.event.EventListenerSource; 20 import org.as2lib.util.ArrayUtil; 21 22 /** 23 * {@code TypeSafeEventListenerSource} manages listeners in a type-safe manner. 24 * 25 * @author Simon Wacker 26 */ 27 class org.as2lib.env.event.TypeSafeEventListenerSource extends BasicClass implements EventListenerSource { 28 29 /** The expected listener type. */ 30 private var t:Function; 31 32 /** All added listeners. */ 33 private var l:Array; 34 35 /** Determines whether to check for the correct listener type. */ 36 private var c:Boolean; 37 38 /** 39 * Constructs a new {@code TypeSafeEventListenerSource} instance. 40 * 41 * <p>{@code checkListenerType} is by default set to {@code true}. 42 * 43 * @param listenerType the expected type of listeners 44 * @param checkListenerType determines whether to check that passed-in listeners 45 * are of the expected type 46 * @throws IllegalArgumentException if the passed-in {@code listenerType} is 47 * {@code null} or {@code undefined} 48 */ 49 public function TypeSafeEventListenerSource(listenerType:Function, checkListenerType:Boolean) { 50 if (!listenerType) throw new IllegalArgumentException("Argument 'listenerType' [" + listenerType + "] must not be 'null' nor 'undefined'.", this, arguments); 51 this.t = listenerType; 52 this.c = checkListenerType == null ? true : checkListenerType; 53 this.l = new Array(); 54 } 55 56 /** 57 * Returns the expected listener type. 58 * 59 * @return the expected listener type 60 */ 61 public function getListenerType(Void):Function { 62 return this.t; 63 } 64 65 /** 66 * Returns whether a listener's type is checked up on the expected listener type. 67 * 68 * @return {@code true} a listener's type is checked else {@code false} 69 */ 70 public function isListenerTypeChecked(Void):Boolean { 71 return this.c; 72 } 73 74 /** 75 * Adds the passed-in {@code listener}. 76 * 77 * <p>The listener will only be added if it is neither {@code null} nor 78 * {@code undefined} and if it is of the expected listener type specified on 79 * construction and if it has not already been added to this listener source. 80 * 81 * <p>Note that the listener type will not be checked if it was turned of on 82 * construction. 83 * 84 * @param listener the listener to add 85 * @throws IllegalArgumentException if the passed-in {@code listener} is not of the 86 * expected type specified on construction 87 */ 88 public function addListener(listener):Void { 89 if (listener) { 90 if (this.c) { 91 if (!(listener instanceof this.t)) { 92 throw new IllegalArgumentException("Argument 'listener' [" + listener + "] must be an instance of the expected listener type [" + this.t + "].", this, arguments); 93 } 94 } 95 if (!hasListener(listener)) { 96 this.l.push(listener); 97 } 98 } 99 } 100 101 /** 102 * Adds all listeners contained in the passed-in {@code listeners} array. 103 * 104 * <p>If the passed-in {@code listeners} array is {@code null} or {@code undefined} 105 * it will be ignored. 106 * 107 * <p>The individual listeners must be instances of the type specified on 108 * construction. If an individual listener is {@code null} or {@code undefined} it 109 * will be ignored. 110 * 111 * <p>All listeners that are of the correct type will be added. 112 * 113 * <p>Note that the listener type will not be checked if it was turned of on 114 * construction. 115 * 116 * <p>Note also that the order of the listeners contained in the passed-in 117 * {@code listeners} array is preserved. 118 * 119 * @param listeners the listeners to add 120 * @throws IllegalArgumentException if at least one listener in the passed-in 121 * {@code listeners} array is not of the expected type specified on construction 122 * @see #addListener 123 */ 124 public function addAllListeners(listeners:Array):Void { 125 if (listeners) { 126 if (this.c) { 127 var exceptions:Array; 128 var h:Number = listeners.length; 129 // the original order of the passed-in 'listeners' is preserved 130 for (var i:Number = 0; i < h; i++) { 131 try { 132 addListener(listeners[i]); 133 } catch (e:org.as2lib.env.except.IllegalArgumentException) { 134 // this case probably hardly ever occurs; the array is thus only instantiated if 135 // really necessary 136 if (!exceptions) exceptions = new Array(); 137 exceptions.push(e); 138 } 139 } 140 if (exceptions) { 141 // source this out in own exception; maybe IllegalListenerException? 142 var message:String = IllegalArgumentException(exceptions[0]).getMessage(); 143 for (var k:Number = 1; k < exceptions.length; k++) { 144 message += IllegalArgumentException(exceptions[k]).getMessage(); 145 } 146 throw new IllegalArgumentException(message, this, arguments); 147 } 148 } else { 149 var h:Number = listeners.length; 150 // the original order of the passed-in 'listeners' is preserved 151 for (var i:Number = 0; i < h; i++) { 152 addListener(listeners[i]); 153 } 154 } 155 } 156 } 157 158 /** 159 * Removes the passed-in {@code listener}. 160 * 161 * <p>The removal will be ignored if the passed-in {@code listener} is {@code null} 162 * or {@code undefined}. 163 * 164 * @param listener the listener to remove 165 */ 166 public function removeListener(listener):Void { 167 if (listener) { 168 var i:Number = this.l.length; 169 while (--i > -1) { 170 if (this.l[i] == listener) { 171 this.l.splice(i, 1); 172 return; 173 } 174 } 175 } 176 } 177 178 /** 179 * Removes all added listeners. 180 */ 181 public function removeAllListeners(Void):Void { 182 this.l = new Array(); 183 } 184 185 /** 186 * Returns all added listeners that are of the type specified on construction. 187 * 188 * @return all added listeners 189 */ 190 public function getAllListeners(Void):Array { 191 return this.l.concat(); 192 } 193 194 /** 195 * Returns {@code true} if passed-in {@code listener} has been added. 196 * 197 * @param listener the listener to check whether it has been added 198 * @return {@code true} if the {@code listener} has been added 199 */ 200 public function hasListener(listener):Boolean { 201 return ArrayUtil.contains(this.l, listener); 202 } 203 204 }