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.overload.Overload; 19 import org.as2lib.data.holder.List; 20 import org.as2lib.util.Stringifier; 21 import org.as2lib.data.holder.list.ListStringifier; 22 import org.as2lib.data.holder.list.SubList; 23 import org.as2lib.data.holder.IndexOutOfBoundsException; 24 25 /** 26 * {@code AbstractList} provides common implementations of methods needed by 27 * implementations of the {@link List} interface. 28 * 29 * @author Simon Wacker 30 */ 31 class org.as2lib.data.holder.list.AbstractList extends BasicClass { 32 33 /** Stringifies lists. */ 34 private static var stringifier:Stringifier; 35 36 /** 37 * Returns the stringifier to stringify lists. 38 * 39 * @return the list stringifier 40 */ 41 public static function getStringifier(Void):Stringifier { 42 if (!stringifier) stringifier = new ListStringifier(); 43 return stringifier; 44 } 45 46 /** 47 * Sets the stringifier to stringify lists. 48 * 49 * @param listStringifier the stringifier to stringify lists 50 */ 51 public static function setStringifier(listStringifier:Stringifier):Void { 52 stringifier = listStringifier; 53 } 54 55 /** This instance casted to interface {@code List}. */ 56 private var thiz:List; 57 58 /** 59 * Constructs a new {@code AbstractList} instance. 60 */ 61 private function AbstractList(Void) { 62 thiz = List(this); 63 } 64 65 /** 66 * @overload #insertByValue 67 * @overload insertByIndexAndValue 68 */ 69 public function insert():Void { 70 var o:Overload = new Overload(this); 71 o.addHandler([Object], insertByValue); 72 o.addHandler([Number, Object], thiz.insertByIndexAndValue); 73 o.forward(arguments); 74 } 75 76 /** 77 * Inserts {@code value} at the end of this list. 78 * 79 * @param value the value to insert 80 * @see #insertLast 81 */ 82 public function insertByValue(value):Void { 83 insertLast(value); 84 } 85 86 /** 87 * Inserts {@code value} at the beginning of this list. 88 * 89 * @param value the value to insert 90 */ 91 public function insertFirst(value):Void { 92 thiz.insertByIndexAndValue(0, value); 93 } 94 95 /** 96 * Inserts {@code value} at the end of this list. 97 * 98 * @param value the value to insert 99 * @see #insert 100 */ 101 public function insertLast(value):Void { 102 thiz.insertByIndexAndValue(thiz.size(), value); 103 } 104 105 /** 106 * @overload #insertAllByList 107 * @overload #insertAllByIndexAndList 108 */ 109 public function insertAll():Void { 110 var o:Overload = new Overload(this); 111 o.addHandler([List], insertAllByList); 112 o.addHandler([Number, List], insertAllByIndexAndList); 113 o.forward(arguments); 114 } 115 116 /** 117 * Inserts all values contained in {@code list} to the end of this list. 118 * 119 * @param list the values to insert 120 */ 121 public function insertAllByList(list:List):Void { 122 var v:Array = list.toArray(); 123 var l:Number = v.length; 124 for (var i:Number = 0; i < l; i++) { 125 thiz.insertLast(v[i]); 126 } 127 } 128 129 /** 130 * Inserts all values contained in {@code list} to this list, starting at the 131 * specified {@code index}. 132 * 133 * <p>Elements that are at an affected index are shifted to the right by the size 134 * of the given {@code list}. 135 * 136 * @param index the index to start the insertion at 137 * @param list the values to insert 138 * @throws IndexOutOfBoundsException if the given {@code index} is not in range, 139 * this is less than 0 or greater than this list's size 140 */ 141 public function insertAllByIndexAndList(index:Number, list:List):Void { 142 if (index < 0 || index > thiz.size()) { 143 throw new IndexOutOfBoundsException("Argument 'index' [" + index + "] is out of range, this is less than 0 or greater than this list's size [" + thiz.size() + "].", this, arguments); 144 } 145 var v:Array = list.toArray(); 146 var l:Number = v.length; 147 for (var i:Number = 0; i < l; i++) { 148 thiz.insertByIndexAndValue(i + index, v[i]); 149 } 150 } 151 152 /** 153 * @overload #removeByValue 154 * @overload removeByIndex 155 */ 156 public function remove() { 157 var o:Overload = new Overload(this); 158 o.addHandler([Object], removeByValue); 159 o.addHandler([Number], thiz.removeByIndex); 160 return o.forward(arguments); 161 } 162 163 /** 164 * Removes {@code value} from this list if it exists. 165 * 166 * @param value the value to remove 167 */ 168 public function removeByValue(value):Number { 169 var result:Number = indexOf(value); 170 if (result > -1) { 171 thiz.removeByIndex(indexOf(value)); 172 } 173 return result; 174 } 175 176 /** 177 * Removes the value at the beginning of this list. 178 * 179 * @return the removed value 180 */ 181 public function removeFirst(Void) { 182 return thiz.removeByIndex(0); 183 } 184 185 /** 186 * Removes the value at the end of this list. 187 * 188 * @return the removed value 189 */ 190 public function removeLast(Void) { 191 return thiz.removeByIndex(thiz.size() - 1); 192 } 193 194 /** 195 * Removes all values contained in {@code list}. 196 * 197 * @param list the values to remove 198 */ 199 public function removeAll(list:List):Void { 200 var v:Array = list.toArray(); 201 var l:Number = v.length; 202 for (var i:Number = 0; i < l; i++) { 203 removeByValue(v[i]); 204 } 205 } 206 207 /** 208 * Sets all values contained in {@code list} to this list, starting from given 209 * {@code index}. They values that were originally at the given {@code index} 210 * and following indices will be overwritten. 211 * 212 * <p>This method only overwrites existing index-value pairs. If an affected index 213 * is equal to or greater than this list's size, which would mean that this list's 214 * size had to be expanded, an {@code IndexOutOfBoundsException} will be thrown. In 215 * such a case use the {@link #insertAll} method instead, which expands this list 216 * dynamically. 217 * 218 * @param index the index to start at 219 * @param list the values to set 220 * @throws IndexOutOfBoundsException if given {@code index} is less than 0 or if 221 * any affected index, that is the given {@code index} plus the index of the 222 * specific value in the given {@code list}, is equal to or greater than this list's 223 * size 224 */ 225 public function setAll(index:Number, list:List):Void { 226 if (index < 0 || index + list.size() > thiz.size()) { 227 throw new IndexOutOfBoundsException("Argument 'index' [" + index + "] is out of range, this is less than 0 or the 'index' plus the size of the given 'list' [" + list.size() + "] is greater than this list's size [" + thiz.size() + "].", this, arguments); 228 } 229 var v:Array = list.toArray(); 230 var l:Number = v.length; 231 for (var i:Number = 0; i < l; i++) { 232 thiz.set(index++, v[i]); 233 } 234 } 235 236 /** 237 * Retains all values the are contained in {@code list} and removes all others. 238 * 239 * @param list the list of values to retain 240 */ 241 public function retainAll(list:List):Void { 242 var i:Number = thiz.size(); 243 while(--i-(-1)) { 244 if (!list.contains(thiz.get(i))) { 245 thiz.removeByIndex(i); 246 } 247 } 248 } 249 250 /** 251 * Checks whether {@code value} is contained in this list. 252 * 253 * @param value the value to check whether it is contained 254 * @return {@code true} if {@code value} is contained else {@code false} 255 */ 256 public function contains(value):Boolean { 257 return (indexOf(value) > -1); 258 } 259 260 /** 261 * Checks whether all values of {@code list} are contained in this list. 262 * 263 * @param list the values to check whether they are contained 264 * @return {@code true} if all values of {@code list} are contained else 265 * {@code false} 266 */ 267 public function containsAll(list:List):Boolean { 268 var v:Array = list.toArray(); 269 var l:Number = v.length; 270 for (var i:Number = 0; i < l; i++) { 271 if (!contains(v[i])) { 272 return false; 273 } 274 } 275 return true; 276 } 277 278 /** 279 * Returns a view of the portion of this list between the specified {@code fromIndex}, 280 * inclusive, and {@code toIndex}, exclusive. 281 * 282 * <p>If {@code fromIndex} and {@code toIndex} are equal an empty list is returned. 283 * 284 * <p>The returned list is backed by this list, so changes in the returned list are 285 * reflected in this list, and vice-versa. 286 * 287 * @param fromIndex the index from which the sub-list starts (inclusive) 288 * @param toIndex the index specifying the end of the sub-list (exclusive) 289 * @return a view of the specified range within this list 290 * @throws IndexOutOfBoundsException if argument {@code fromIndex} is less than 0 291 * @throws IndexOutOfBoundsException if argument {@code toIndex} is greater than 292 * the size of this list 293 * @throws IndexOutOfBoundsException if argument {@code fromIndex} is greater than 294 * {@code toIndex} 295 */ 296 public function subList(fromIndex:Number, toIndex:Number):List { 297 return new SubList(thiz, fromIndex, toIndex); 298 } 299 300 /** 301 * Returns the index of {@code value}. 302 * 303 * @param value the value to return the index of 304 * @return the index of {@code value} 305 */ 306 public function indexOf(value):Number { 307 var l:Number = thiz.size(); 308 while (--l > -1 && thiz.get(l) !== value); 309 return l; 310 } 311 312 /** 313 * Returns whether this list is empty. 314 * 315 * <p>This list is empty if it has no values assigned to it. 316 * 317 * @return {@code true} if this list is empty else {@code false} 318 */ 319 public function isEmpty(Void):Boolean { 320 return (thiz.size() < 1); 321 } 322 323 /** 324 * Returns the string representation of this list. 325 * 326 * <p>The string representation is obtained via the stringifier returned by the 327 * static {@link #getStringifier} method. 328 * 329 * @return the string representation of this list 330 */ 331 public function toString():String { 332 return getStringifier().execute(this); 333 } 334 335 }