001/** 002 * DataElementList -- Interface in common to all data element lists. 003 * 004 * Copyright (C) 2003-2015, by Joseph A. Huwaldt. All rights reserved. 005 * 006 * This library is free software; you can redistribute it and/or modify it under the terms 007 * of the GNU Lesser General Public License as published by the Free Software Foundation; 008 * either version 2 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 012 * PARTICULAR PURPOSE. See the GNU Library General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License along with 015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - 016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html 017 */ 018package jahuwaldt.js.datareader; 019 020import java.text.MessageFormat; 021import java.util.AbstractList; 022import javolution.lang.Reusable; 023import javolution.text.Text; 024import javolution.util.FastTable; 025 026/** 027 * A named list of {@link DataElement} objects with associated user data. 028 * 029 * <p> Modified by: Joseph A. Huwaldt </p> 030 * 031 * @author Joseph A. Huwaldt, Date: March 5, 2003 032 * @version October 15, 2015 033 */ 034public abstract class DataElementList<E extends DataElement> extends AbstractList<E> implements DataElement, Reusable { 035 036 // The list behind this implementation. We must do this rather than extend FastTable directly 037 // since methods that we need to override are final. 038 private final FastTable<E> _list = FastTable.newInstance(); 039 040 /** 041 * Name of this geometry element. 042 */ 043 private CharSequence _name; 044 045 // Reference data for this element. 046 private Object _userData; 047 048 /** 049 * Do not allow the default constructor to be used except by subclasses. 050 */ 051 protected DataElementList() { } 052 053 /** 054 * Returns the number of elements in this list. If the list contains more than 055 * Integer.MAX_VALUE elements, returns Integer.MAX_VALUE. 056 * 057 * @return the number of elements in this list. 058 */ 059 @Override 060 public int size() { 061 return _list.size(); 062 } 063 064 /** 065 * Returns the element at the specified position in this list. 066 * 067 * @param index index of element to return. 068 * @return the element at the specified position in this list. 069 * @throws IndexOutOfBoundsException if the given index is out of range (index < 0 070 * || index > size()-1) 071 */ 072 @Override 073 public E get(int index) { 074 return _list.get(index); 075 } 076 077 /** 078 * Replaces the {@link DataElement} at the specified position in this list with the 079 * specified element. Null elements are ignored. 080 * 081 * @param index The index of the element to replace. 082 * @param element The element to be stored at the specified position. 083 * @return The element previously at the specified position in this list. 084 * @throws java.lang.IndexOutOfBoundsException - if (index < 0) || (index > 085 * size()-1) 086 */ 087 @Override 088 public E set(int index, E element) { 089 if (element == null) 090 return null; 091 092 return _list.set(index, element); 093 } 094 095 /** 096 * Inserts the specified {@link DataElement} at the specified position in this list. 097 * Shifts the element currently at that position (if any) and any subsequent elements 098 * to the right (adds one to their indices). Null values are ignored. 099 * <p> 100 * Note: If this method is used, concurrent access must be synchronized (the table is 101 * not thread-safe). 102 * </p> 103 * 104 * @param index the index at which the specified element is to be inserted. 105 * @param value the element to be inserted. 106 * @throws IndexOutOfBoundsException if 107 * <code>(index < 0) || (index > size()-1)</code> 108 */ 109 @Override 110 public void add(int index, E value) { 111 if (value == null) 112 return; 113 _list.add(index, value); 114 } 115 116 /** 117 * Appends the specified list of elements to the end of this list. Null elements are 118 * ignored. 119 * 120 * @param elements Array of elements to be inserted. 121 */ 122 public void addAll(E[] elements) { 123 if (elements != null) { 124 int size = elements.length; 125 for (int i = 0; i < size; ++i) { 126 E element = elements[i]; 127 this.add(element); 128 } 129 130 } 131 } 132 133 /** 134 * Inserts the specified list of elements at the specified position in this list. 135 * Shifts the elements currently at that position (if any) and any subsequent 136 * parameters to the right (adds one to their indices) until all the elements have 137 * been added. 138 * 139 * @param index Index at which the specified list of elements is to be inserted. 140 * @param elements List of elements to be inserted. 141 */ 142 public void addAll(int index, E[] elements) { 143 if (elements != null) { 144 int size = elements.length; 145 for (int i = 0; i < size; ++i) { 146 E element = elements[i]; 147 this.add(index, element); 148 ++index; 149 } 150 151 } 152 } 153 154 /** 155 * Removes the element at the specified position in this list. Shifts any subsequent 156 * elements to the left (subtracts one from their indices). Returns the element that 157 * was removed from the list. 158 * 159 * @param index the index of the element to remove. 160 * @return the element previously at the specified position. 161 */ 162 @Override 163 public E remove(int index) { 164 return _list.remove(index); 165 } 166 167 /** 168 * Removes from this list all of the elements whose index is between fromIndex, 169 * inclusive, and toIndex, exclusive. This method is called by the <code>clear</code> 170 * operation on this list and its subLists. 171 * 172 * @see #clear() 173 */ 174 @Override 175 protected void removeRange(int fromIndex, int toIndex) { 176 _list.removeRange(fromIndex, toIndex); 177 } 178 179 /** 180 * Returns an iterator over the elements in this list (allocated on the stack when 181 * executed in a StackContext). 182 * 183 * @return an iterator over this list values. 184 */ 185 @Override 186 public java.util.Iterator<E> iterator() { 187 return _list.iterator(); 188 } 189 190 /** 191 * Returns a list iterator over the elements in this list (allocated on the stack when 192 * executed in a StackContext). 193 * 194 * @return an iterator over this list values. 195 */ 196 @Override 197 public java.util.ListIterator<E> listIterator() { 198 return _list.listIterator(); 199 } 200 201 /** 202 * Returns a list iterator from the specified position (allocated on the stack when 203 * executed in a StackContext). The list iterator being returned does not support 204 * insertion/deletion. 205 * 206 * @param index the index of first value to be returned from the list iterator (by a 207 * call to the next method). 208 * @return a list iterator of the values in this table starting at the specified 209 * position in this list. 210 */ 211 @Override 212 public java.util.ListIterator<E> listIterator(int index) { 213 return _list.listIterator(index); 214 } 215 216 /** 217 * Returns the unmodifiable view associated to this collection. Attempts to modify the 218 * returned collection result in an UnsupportedOperationException being thrown. The 219 * view is typically part of the collection itself (created only once) and also an 220 * instance of FastCollection supporting direct iterations. 221 * 222 * @return the unmodifiable view over this collection. 223 */ 224 public java.util.List<E> unmodifiable() { 225 return _list.unmodifiable(); 226 } 227 228 /** 229 * Resets the internal state of this object to its default values. 230 */ 231 @Override 232 public void reset() { 233 _list.reset(); 234 _userData = null; 235 _name = null; 236 } 237 238 /** 239 * Returns the element with the specified name from this list. 240 * 241 * @param name The name of the element we are looking for in the list. 242 * @return The element matching the specified name. If the specified element name 243 * isn't found in the list, then null is returned. 244 */ 245 public E get(CharSequence name) { 246 247 E element = null; 248 int index = getIndexFromName(name); 249 if (index >= 0) 250 element = this.get(index); 251 252 return element; 253 } 254 255 /** 256 * Removes the element at the specified name in this list. Shifts any subsequent 257 * elements to the left (subtracts one from their indices). Returns the element that 258 * was removed from the list. 259 * 260 * @param name the name of the element to remove. 261 * @return the element previously at the specified position. 262 */ 263 public E remove(CharSequence name) { 264 265 E element = null; 266 int index = getIndexFromName(name); 267 if (index >= 0) 268 element = this.remove(index); 269 270 return element; 271 } 272 273 /** 274 * Return the index to the 1st data element in this list with the specified name. 275 * 276 * @param name The name of the data element to find in this list. 277 * @return The index to the named data element or -1 if it is not found. 278 */ 279 public int getIndexFromName(CharSequence name) { 280 Text nameText = Text.valueOf(name); 281 282 int result = -1; 283 int size = this.size(); 284 for (int i = 0; i < size; ++i) { 285 DataElement element = this.get(i); 286 Text eName = Text.valueOf(element.getName()); 287 if (eName.equals(nameText)) { 288 result = i; 289 break; 290 } 291 } 292 return result; 293 } 294 295 // ******* The following support data element requirements ***** 296 /** 297 * Return any user defined object associated with this data element. If there is no 298 * user data, then <code>null</code> is returned. 299 */ 300 @Override 301 public Object getUserObject() { 302 return _userData; 303 } 304 305 /** 306 * Set the user defined object associated with this data element. This can be used to 307 * store any type of information with a data element that could be useful. Storing 308 * <code>null</code> for no user object is fine. 309 */ 310 @Override 311 public void setUserObject(Object data) { 312 _userData = data; 313 } 314 315 /** 316 * Return the name of this data element. 317 */ 318 @Override 319 public CharSequence getName() { 320 return _name; 321 } 322 323 /** 324 * Change the name of this data element to the specified name (may not be 325 * <code>null</code>). 326 */ 327 @Override 328 public void setName(CharSequence name) { 329 if (name == null) 330 throw new NullPointerException(MessageFormat.format( 331 RESOURCES.getString("paramNullErr"),"unit")); 332 _name = name; 333 } 334 335 /** 336 * Compares the specified object with this list of <code>DataElement</code> objects 337 * for equality. Returns true if and only if both collections contain equal values in 338 * the same order. 339 * 340 * @param obj the object to compare with. 341 * @return <code>true</code> if this list is identical to that list; 342 * <code>false</code> otherwise. 343 */ 344 @Override 345 public boolean equals(Object obj) { 346 if (this == obj) 347 return true; 348 if ((obj == null) || (obj.getClass() != this.getClass())) 349 return false; 350 351 DataElementList<?> that = (DataElementList<?>)obj; 352 if (!this._name.equals(that._name)) 353 return false; 354 if (this._userData == null) { 355 if (that._userData != null) 356 return false; 357 } else if (!this._userData.equals(that._userData)) 358 return false; 359 360 return _list.equals(obj); 361 } 362 363 /** 364 * Returns the hash code for this <code>DataElementList</code>. 365 * 366 * @return the hash code value. 367 */ 368 @Override 369 public int hashCode() { 370 int hash = _list.hashCode(); 371 372 hash = hash * 31 + _name.hashCode(); 373 hash = hash * 31 + (_userData != null ? _userData.hashCode() : 0); 374 375 return hash; 376 } 377 378 /** 379 * Create a Text representation of this data element which simply consists of the 380 * element's name. 381 */ 382 public Text toText() { 383 return Text.valueOf(getName()); 384 } 385 386 /** 387 * Create a string representation of this data element which simply consists of the 388 * element's name. 389 */ 390 @Override 391 public String toString() { 392 return getName().toString(); 393 } 394 395 /* 396 * Compares this data element with the specified element for order (where 397 * order is determined by the element's name). 398 * Returns a negative integer, zero, or a positive integer as this 399 * object is less than, equal to, or greater than the specified object. 400 * This method delegates to the Text.compareTo() method. 401 * 402 * @param otherElement The data element this one is being compared to. 403 * @throw ClassCastException if the specified object's type prevents 404 * it from being compared to this Object. 405 */ 406 @Override 407 public int compareTo(DataElement otherElement) { 408 Text thisName = Text.valueOf(this.getName()); 409 Text otherName = Text.valueOf(otherElement.getName()); 410 return thisName.compareTo(otherName); 411 } 412}