001/* A list of unique objects with an efficient mapping from the objects 002 into consecutive integer labels. 003 004 Copyright (c) 2001-2015 The University of Maryland. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF MARYLAND BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF MARYLAND HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF MARYLAND SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 MARYLAND HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 029 */ 030package ptolemy.graph; 031 032import java.util.ArrayList; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.Iterator; 037import java.util.List; 038import java.util.ListIterator; 039 040/////////////////////////////////////////////////////////////////// 041//// LabeledList 042 043/** A list of unique objects (<i>elements</i>) with a mapping from the 044 elements into consecutive integer labels. The labels are consecutive 045 integers between 0 and <em>N</em>-1 inclusive, where <em>N</em> is 046 the total number of elements in the list. This list features 047 <em>O</em>(1) list insertion, <em>O</em>(1) testing for membership in 048 the list, <em>O</em>(1) access of a list element from its associated 049 label, and <em>O</em>(1) access of a label from its corresponding 050 element. Removal from the list is, however, an 051 <em>O</em>(<em>1</em>) operation. The element labels are useful, for 052 example, in creating mappings from list elements into elements of 053 arbitrary arrays. More generally, element labels can be used to 054 maintain arbitrary <em>m</em>-dimensional matrices that are indexed 055 by the list elements (via the associated element labels). 056 057 <p> Element labels maintain their consistency (remain constant) during 058 periods when no elements are removed from the list. When elements are 059 removed, the labels assigned to the remaining elements may 060 change (see {@link #remove(Object)} for details). 061 062 <p> Elements themselves must be non-null and distinct, as determined by the 063 <code>equals</code> method. 064 065 <p> This class supports all required operations of 066 the {@link java.util.List} interface, except 067 for the {@link java.util.List#subList(int, int)} operation, 068 which results in an UnsupportedOperationException. 069 070 @author Shuvra S. Bhattacharyya 071 @version $Id$ 072 @since Ptolemy II 2.0 073 @Pt.ProposedRating Red (cxh) 074 @Pt.AcceptedRating Red (cxh) 075 */ 076public class LabeledList implements List { 077 /** Construct an empty list. 078 */ 079 public LabeledList() { 080 _elements = new ArrayList(0); 081 _labels = new HashMap(0); 082 } 083 084 /** Construct an empty list with enough storage allocated to hold 085 * the specified number of elements. Memory management is more 086 * efficient with this constructor (assuming the number of elements is 087 * known). 088 * @param size The number of elements. 089 */ 090 public LabeledList(int size) { 091 _elements = new ArrayList(size); 092 _labels = new HashMap(size); 093 } 094 095 /////////////////////////////////////////////////////////////////// 096 //// public methods //// 097 098 /** Add an element to the list. The label assigned to this element 099 * will be equal to the number of elements in the list 100 * prior to insertion of the element. 101 * @param element The element to insert. 102 * @return True unconditionally (assuming an exception does not occur). 103 * @exception IllegalArgumentException If the specified element is null, 104 * or if it already exists in the list. 105 */ 106 @Override 107 public boolean add(Object element) { 108 if (element == null) { 109 throw new IllegalArgumentException( 110 "Attempt to insert a null " + "element"); 111 } else if (_labels.containsKey(element)) { 112 throw new IllegalArgumentException("Attempt to insert a duplicate " 113 + "element." + _elementDump(element)); 114 } else { 115 _labels.put(element, Integer.valueOf(_elements.size())); 116 _elements.add(element); 117 return true; 118 } 119 } 120 121 /** Unsupported optional method of the List interface. 122 * @param index Unused. 123 * @param element Unused. 124 * @exception UnsupportedOperationException Always thrown. 125 */ 126 @Override 127 public void add(int index, Object element) { 128 throw new UnsupportedOperationException(); 129 } 130 131 /** Unsupported optional method of the List interface. 132 * @param collection Unused. 133 * @exception UnsupportedOperationException Always thrown. 134 * @return never returns. 135 */ 136 @Override 137 public boolean addAll(Collection collection) { 138 throw new UnsupportedOperationException(); 139 } 140 141 /** Unsupported optional method of the List interface. 142 * @param index Unused. 143 * @param collection Unused. 144 * @exception UnsupportedOperationException Always thrown. 145 * @return never returns. 146 */ 147 @Override 148 public boolean addAll(int index, Collection collection) { 149 throw new UnsupportedOperationException(); 150 } 151 152 /** Clear all of the elements in this list. 153 */ 154 @Override 155 public void clear() { 156 _elements.clear(); 157 _labels.clear(); 158 } 159 160 /** Return true if the specified object is an element of this list. 161 * @param object The specified object. 162 * @return True if the specified object is an element of this list; 163 * false if the object is null or is not in the list. 164 */ 165 @Override 166 public boolean contains(Object object) { 167 if (object == null) { 168 return false; 169 } else { 170 return _labels.containsKey(object); 171 } 172 } 173 174 /** Returns true if this list contains all of the elements of the 175 * specified collection. 176 * @param collection The specified collection. 177 * @return True if this list contains all of the elements of the 178 * specified collection. 179 */ 180 @Override 181 public boolean containsAll(Collection collection) { 182 Iterator elements = collection.iterator(); 183 184 while (elements.hasNext()) { 185 if (!contains(elements.next())) { 186 return false; 187 } 188 } 189 190 return true; 191 } 192 193 /** Compares the specified object with this list for equality. 194 * @param object The object. 195 * @return True if the specified object is equal to this list. 196 */ 197 @Override 198 public boolean equals(Object object) { 199 return _elements.equals(object); 200 } 201 202 /** Return the element that has a specified label. 203 * @param label The label. 204 * @return The element. 205 * @exception IndexOutOfBoundsException If there is no element that 206 * has the specified label. 207 */ 208 @Override 209 public Object get(int label) { 210 if (label < 0 || label >= _elements.size()) { 211 throw new IndexOutOfBoundsException("Invalid label: " + label); 212 } 213 214 return _elements.get(label); 215 } 216 217 /** Return the hash code value for this list. 218 * @return The hash code value. 219 */ 220 @Override 221 public int hashCode() { 222 return _elements.hashCode(); 223 } 224 225 /** Return the label in this list of the specified 226 * element; return -1 if the element is null or this list does not 227 * contain the element. 228 * @param element The element. 229 * @return The label of the element. 230 * @see #label(Object) 231 */ 232 @Override 233 public int indexOf(Object element) { 234 if (element == null) { 235 return -1; 236 } else { 237 Integer label = (Integer) _labels.get(element); 238 239 if (label == null) { 240 return -1; 241 } else { 242 return label.intValue(); 243 } 244 } 245 } 246 247 /** Returns true if this list contains no elements. 248 * @return True if this list contains no elements. 249 */ 250 @Override 251 public boolean isEmpty() { 252 return size() == 0; 253 } 254 255 /** Return an iterator over the elements in the list. The iterator 256 * returned is safe in that it cannot be used to modify the list. 257 * FIXME: what happens when you try to modify the list. 258 * @return An iterator over the elements in the list; 259 */ 260 @Override 261 public Iterator iterator() { 262 return Collections.unmodifiableList(_elements).iterator(); 263 } 264 265 /** Return the label of the specified element. 266 * @param element The specified element. 267 * @return The corresponding label. 268 * @exception IllegalArgumentException If the specified element is not 269 * in this list. 270 * @exception NullPointerException If the specified element is null. 271 * @see #indexOf(Object) 272 */ 273 public final int label(Object element) { 274 if (element == null) { 275 throw new NullPointerException("Null element specified."); 276 } else { 277 Integer label = (Integer) _labels.get(element); 278 279 if (label == null) { 280 throw new IllegalArgumentException("The specified object is not" 281 + " an element of this list. " + _elementDump(element)); 282 } else { 283 return label.intValue(); 284 } 285 } 286 } 287 288 /** Returns the index in this list of the last occurrence of the specified 289 * element, or -1 if this list does not contain this element. 290 * Since elements in a labeled list are distinct, this is the same 291 * as {@link #indexOf(Object)}, and is maintained only for conformance 292 * with the list interface. 293 */ 294 @Override 295 public int lastIndexOf(Object element) { 296 return _elements.indexOf(element); 297 } 298 299 /** Return a list iterator over the elements in the list. The iterator 300 * returned is safe in that it cannot be used to modify the list. 301 * @return A list iterator over the elements in the list; 302 */ 303 @Override 304 public ListIterator listIterator() { 305 return Collections.unmodifiableList(_elements).listIterator(); 306 } 307 308 /** Return a list iterator over the elements in this list, starting 309 * at a specified position in the list. The iterator 310 * returned is safe in that it cannot be used to modify the list. 311 * @param index The specified starting position. 312 * @return A list iterator over the elements in the list; 313 */ 314 @Override 315 public ListIterator listIterator(int index) { 316 return Collections.unmodifiableList(_elements).listIterator(index); 317 } 318 319 /** Remove an element from this list. 320 * Elements that have higher-valued 321 * labels than this element will have their labels reduced in value 322 * by one. All other element labels will remain unchanged. 323 * If the specified element is not in the list, the list will be 324 * unchanged. 325 * FIXME: leave indices indeterminate after removal. 326 * @param element The element. 327 * @return True If this list contained the element. 328 */ 329 @Override 330 public boolean remove(Object element) { 331 int label; 332 333 try { 334 label = label(element); 335 } catch (IllegalArgumentException exception) { 336 throw new IllegalArgumentException("Attempt to remove a " 337 + "non-existent element. " + _elementDump(element)); 338 } 339 340 _labels.remove(element); 341 _elements.remove(label); 342 _labelElements(label); 343 return true; 344 } 345 346 /** Remove and return an element with a specified label from this list. 347 * Elements that have higher-valued 348 * labels than this element will have their labels reduced in value 349 * by one. All other element labels will remain unchanged. 350 * @param label The specified label. 351 * @return The element that is removed. 352 * @exception IndexOutOfBoundsException If there is no element with 353 * the specified label. 354 */ 355 @Override 356 public Object remove(int label) { 357 Object element = get(label); 358 _labels.remove(element); 359 360 Object removed = _elements.remove(label); 361 _labelElements(label); 362 return removed; 363 } 364 365 /** Unsupported optional method of the List interface. 366 * @param collection Unused. 367 * @return never returns. 368 * @exception UnsupportedOperationException Always thrown. 369 */ 370 @Override 371 public boolean removeAll(Collection collection) { 372 throw new UnsupportedOperationException(); 373 } 374 375 /** Unsupported optional method of the List interface. 376 * @param collection Unused. 377 * @return never returns. 378 * @exception UnsupportedOperationException Always thrown. 379 */ 380 @Override 381 public boolean retainAll(Collection collection) { 382 throw new UnsupportedOperationException(); 383 } 384 385 /** Unsupported optional method of the List interface. 386 * @param index Unused. 387 * @param element Unused. 388 * @return never returns. 389 * @exception UnsupportedOperationException Always thrown. 390 */ 391 @Override 392 public Object set(int index, Object element) { 393 throw new UnsupportedOperationException(); 394 } 395 396 /** Return the number of elements in this list. 397 * @return The number of elements. 398 */ 399 @Override 400 public int size() { 401 return _elements.size(); 402 } 403 404 /** Unsupported method of the List interface. 405 * @param fromIndex Unused. 406 * @param toIndex Unused. 407 * @return never returns. 408 * @exception UnsupportedOperationException Always thrown. 409 */ 410 @Override 411 public List subList(int fromIndex, int toIndex) { 412 throw new UnsupportedOperationException(); 413 } 414 415 /** Returns an array containing all of the elements in this list in 416 * proper sequence. 417 * @return An array containing all of the elements in this list. 418 */ 419 @Override 420 public Object[] toArray() { 421 return _elements.toArray(); 422 } 423 424 /** Returns an array containing all of the elements in this list in 425 * proper sequence; the runtime type of the returned array is that of 426 * the specified array. 427 * @param array The specified array. 428 * @return An array containing all of the elements in this list. 429 */ 430 @Override 431 public Object[] toArray(Object[] array) { 432 return _elements.toArray(array); 433 } 434 435 /** Return a string representation of this list, given a delimiter 436 * for separating successive elements, and a flag that indicates 437 * whether element labels should be included in the string. 438 * The string 439 * representation is constructed by concatenating 440 * the string representations of the individual elements, 441 * according to the order of their labels. The element strings 442 * are separated by the specified delimiter, and are optionally 443 * preceded by the associated labels. 444 * @param delimiter The delimiter that separates elements in the 445 * generated string. 446 * @param includeLabels If this is true, then precede each 447 * element with its label (followed by a colon and space) in the 448 * generated string; otherwise, omit the labels. 449 * @return A string representation of this list. 450 */ 451 public String toString(String delimiter, boolean includeLabels) { 452 Iterator elements = iterator(); 453 StringBuffer result = new StringBuffer(); 454 455 while (elements.hasNext()) { 456 Object element = elements.next(); 457 result.append((includeLabels ? label(element) + ": " : "") + element 458 + (elements.hasNext() ? delimiter : "")); 459 } 460 461 return result.toString(); 462 } 463 464 /** Return a string representation of this list with elements separated 465 * by new lines, and element labels omitted from the representation. 466 * The string 467 * representation is constructed by the concatenating 468 * string representations of the individual elements, 469 * according to the order of their labels. The element strings 470 * are separated by newlines. The element labels are not included 471 * in the string representation. 472 * @return A string representation of this list. 473 */ 474 @Override 475 public String toString() { 476 return toString("\n", false); 477 } 478 479 /////////////////////////////////////////////////////////////////// 480 //// private methods //// 481 /** Return a dump of a list element that is suitable for inclusion 482 * in an error message. 483 */ 484 private String _elementDump(Object element) { 485 return "The offending element follows:\n" 486 + (element == null ? "null" : element) + "\n"; 487 } 488 489 /** Fill in the labels map with the appropriate indices of 490 * the array list, starting at a specified index. 491 */ 492 private void _labelElements(int startIndex) { 493 for (int i = startIndex; i < _elements.size(); i++) { 494 _labels.put(_elements.get(i), Integer.valueOf(i)); 495 } 496 } 497 498 /////////////////////////////////////////////////////////////////// 499 //// private variables //// 500 /** The elements that are associated with this list. */ 501 private ArrayList _elements; 502 503 /** Translation from list element to label. The keys of this HashMap 504 * are list elements (instances of Object), and the values are 505 * the corresponding element labels (instances of Integer). 506 * This translation can also be 507 * done with indexOf(), but a HashMap is faster. 508 */ 509 private HashMap _labels; 510}