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 &lt; 0
070     * || index &gt; 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 &lt; 0) || (index &gt;
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 &lt; 0) || (index &gt; 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}