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     */
017    package org.apache.commons.collections.primitives;
018    
019    import java.io.IOException;
020    import java.io.ObjectInputStream;
021    import java.io.ObjectOutputStream;
022    import java.io.Serializable;
023    
024    /**
025     * An {@link ByteList} backed by an array of <code>byte</code>s.
026     * This implementation supports all optional methods.
027     * 
028     * @since Commons Primitives 1.0
029     * @version $Revision: 480460 $ $Date: 2006-11-29 09:14:21 +0100 (Wed, 29 Nov 2006) $
030     * 
031     * @author Rodney Waldhoff 
032     */
033    public class ArrayByteList extends RandomAccessByteList implements ByteList, Serializable {
034    
035        // constructors
036        //-------------------------------------------------------------------------
037    
038        /** 
039         * Construct an empty list with the default
040         * initial capacity.
041         */
042        public ArrayByteList() {
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 ArrayByteList(int initialCapacity) {
052            if(initialCapacity < 0) {
053                throw new IllegalArgumentException("capacity " + initialCapacity);
054            }
055            _data = new byte[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 ArrayByteList#addAll(org.apache.commons.collections.primitives.ByteCollection)
064         * @param that the non-<code>null</code> collection of <code>byte</code>s 
065         *        to add
066         * @throws NullPointerException if <i>that</i> is <code>null</code>
067         */
068        public ArrayByteList(ByteCollection 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 ArrayByteList(byte[] array) { 
080            this(array.length);
081            System.arraycopy(array, 0, _data, 0, array.length);
082            _size = array.length;
083        }
084    
085        // ByteList methods
086        //-------------------------------------------------------------------------
087    
088        public byte 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 byte removeElementAt(int index) {
111            checkRange(index);
112            incrModCount();
113            byte 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 byte set(int index, byte element) {
136            checkRange(index);
137            incrModCount();
138            byte 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, byte 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(ByteCollection collection) {
174            return addAll(size(), collection);
175        }
176    
177        public boolean addAll(int index, ByteCollection 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 (ByteIterator 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                byte[] olddata = _data;
209                _data = new byte[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                byte[] olddata = _data;
222                _data = new byte[_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.writeByte(_data[i]);
235            }
236        }
237    
238        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
239            in.defaultReadObject();
240            _data = new byte[in.readInt()];
241            for(int i=0;i<_size;i++) {
242                _data[i] = in.readByte();
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 byte[] _data = null;
262        private int _size = 0;
263    
264    }