001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections.primitives;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023
024/**
025 * An {@link CharList} backed by an array of <code>char</code>s.
026 * This implementation supports all optional methods.
027 * 
028 * @since Commons Primitives 1.0
029 * @version $Revision: 480460 $ $Date: 2006-11-29 03:14:21 -0500 (Wed, 29 Nov 2006) $
030 * 
031 * @author Rodney Waldhoff 
032 */
033public class ArrayCharList extends RandomAccessCharList implements CharList, Serializable {
034
035    // constructors
036    //-------------------------------------------------------------------------
037
038    /** 
039     * Construct an empty list with the default
040     * initial capacity.
041     */
042    public ArrayCharList() {
043        this(8);
044    }    
045
046    /**
047     * Construct an empty list with the given
048     * initial capacity.
049     * @throws IllegalArgumentException when <i>initialCapacity</i> is negative
050     */
051    public ArrayCharList(int initialCapacity) {
052        if(initialCapacity < 0) {
053            throw new IllegalArgumentException("capacity " + initialCapacity);
054        }
055        _data = new char[initialCapacity];
056        _size = 0;
057    }    
058
059    /** 
060     * Constructs a list containing the elements of the given collection, 
061     * in the order they are returned by that collection's iterator.
062     * 
063     * @see ArrayCharList#addAll(org.apache.commons.collections.primitives.CharCollection)
064     * @param that the non-<code>null</code> collection of <code>char</code>s 
065     *        to add
066     * @throws NullPointerException if <i>that</i> is <code>null</code>
067     */
068    public ArrayCharList(CharCollection that) { 
069        this(that.size());
070        addAll(that);
071    }    
072
073    /**
074     * Constructs a list by copying the specified array.
075     * 
076     * @param array  the array to initialize the collection with
077     * @throws NullPointerException if the array is <code>null</code>
078     */
079    public ArrayCharList(char[] array) { 
080        this(array.length);
081        System.arraycopy(array, 0, _data, 0, array.length);
082        _size = array.length;
083    }
084
085    // CharList methods
086    //-------------------------------------------------------------------------
087
088    public char get(int index) {
089        checkRange(index);
090        return _data[index];
091    }
092    
093    public int size() {
094        return _size;
095    }
096    
097    /** 
098     * Removes the element at the specified position in 
099     * (optional operation).  Any subsequent elements 
100     * are shifted to the left, subtracting one from their 
101     * indices.  Returns the element that was removed.
102     * 
103     * @param index the index of the element to remove
104     * @return the value of the element that was removed
105     * 
106     * @throws UnsupportedOperationException when this operation is not 
107     *         supported
108     * @throws IndexOutOfBoundsException if the specified index is out of range
109     */
110    public char removeElementAt(int index) {
111        checkRange(index);
112        incrModCount();
113        char oldval = _data[index];
114        int numtomove = _size - index - 1;
115        if(numtomove > 0) {
116            System.arraycopy(_data,index+1,_data,index,numtomove);
117        }
118        _size--;
119        return oldval;
120    }
121    
122    /** 
123     * Replaces the element at the specified 
124     * position in me with the specified element
125     * (optional operation). 
126     * 
127     * @param index the index of the element to change
128     * @param element the value to be stored at the specified position
129     * @return the value previously stored at the specified position
130     * 
131     * @throws UnsupportedOperationException when this operation is not 
132     *         supported
133     * @throws IndexOutOfBoundsException if the specified index is out of range
134     */
135    public char set(int index, char element) {
136        checkRange(index);
137        incrModCount();
138        char oldval = _data[index];
139        _data[index] = element;
140        return oldval;
141    }
142        
143    /** 
144     * Inserts the specified element at the specified position 
145     * (optional operation). Shifts the element currently 
146     * at that position (if any) and any subsequent elements to the 
147     * right, increasing their indices.
148     * 
149     * @param index the index at which to insert the element
150     * @param element the value to insert
151     * 
152     * @throws UnsupportedOperationException when this operation is not 
153     *         supported
154     * @throws IllegalArgumentException if some aspect of the specified element 
155     *         prevents it from being added to me
156     * @throws IndexOutOfBoundsException if the specified index is out of range
157     */
158    public void add(int index, char element) {
159        checkRangeIncludingEndpoint(index);
160        incrModCount();
161        ensureCapacity(_size+1);
162        int numtomove = _size-index;
163        System.arraycopy(_data,index,_data,index+1,numtomove);
164        _data[index] = element;
165        _size++;
166    }
167
168    public void clear() {
169        incrModCount();
170        _size = 0;
171    }
172
173    public boolean addAll(CharCollection collection) {
174        return addAll(size(), collection);
175    }
176
177    public boolean addAll(int index, CharCollection collection) {
178        if (collection.size() == 0) {
179            return false;
180        }
181        checkRangeIncludingEndpoint(index);
182        incrModCount();
183        ensureCapacity(_size + collection.size());
184        if (index != _size) {
185            // Need to move some elements
186            System.arraycopy(_data, index, _data, index + collection.size(), _size - index);
187        }
188        for (CharIterator it = collection.iterator(); it.hasNext();) {
189            _data[index] = it.next();
190            index++;
191        }
192        _size += collection.size();
193        return true;
194    }
195
196    // capacity methods
197    //-------------------------------------------------------------------------
198
199    /** 
200     * Increases my capacity, if necessary, to ensure that I can hold at 
201     * least the number of elements specified by the minimum capacity 
202     * argument without growing.
203     */
204    public void ensureCapacity(int mincap) {
205        incrModCount();
206        if(mincap > _data.length) {
207            int newcap = (_data.length * 3)/2 + 1;
208            char[] olddata = _data;
209            _data = new char[newcap < mincap ? mincap : newcap];
210            System.arraycopy(olddata,0,_data,0,_size);
211        }
212    }
213
214    /** 
215     * Reduce my capacity, if necessary, to match my
216     * current {@link #size size}.
217     */
218    public void trimToSize() {
219        incrModCount();
220        if(_size < _data.length) {
221            char[] olddata = _data;
222            _data = new char[_size];
223            System.arraycopy(olddata,0,_data,0,_size);
224        }
225    }
226
227    // private methods
228    //-------------------------------------------------------------------------
229    
230    private void writeObject(ObjectOutputStream out) throws IOException{
231        out.defaultWriteObject();
232        out.writeInt(_data.length);
233        for(int i=0;i<_size;i++) {
234            out.writeChar(_data[i]);
235        }
236    }
237
238    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
239        in.defaultReadObject();
240        _data = new char[in.readInt()];
241        for(int i=0;i<_size;i++) {
242            _data[i] = in.readChar();
243        }
244    }
245    
246    private final void checkRange(int index) {
247        if(index < 0 || index >= _size) {
248            throw new IndexOutOfBoundsException("Should be at least 0 and less than " + _size + ", found " + index);
249        }
250    }
251
252    private final void checkRangeIncludingEndpoint(int index) {
253        if(index < 0 || index > _size) {
254            throw new IndexOutOfBoundsException("Should be at least 0 and at most " + _size + ", found " + index);
255        }
256    }
257
258    // attributes
259    //-------------------------------------------------------------------------
260    
261    private transient char[] _data = null;
262    private int _size = 0;
263
264}