001/*
002 *  Copyright 2001-2010 Stephen Colebourne
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.joda.primitives.list.impl;
017
018import org.joda.primitives.list.CharList;
019
020/**
021 * CharList implementation that uses a StringBuffer internally.
022 * <p>
023 * This class implements {@link java.util.List List} allowing
024 * seamless integration with other APIs.
025 *
026 * @author Stephen Colebourne
027 * @since 1.0
028 */
029public class StringBufferCharList extends AbstractCharList {
030
031    /** The String being wrapped */    
032    protected final StringBuffer stringBuffer;
033    
034    /**
035     * Decorates the specified string buffer with a StringBufferCharList.
036     * <p>
037     * The specified buffer is used as the datasource, so changes to one
038     * will affect the other.
039     * 
040     * @param buf  the string buffer to decorate, must not be null
041     * @return the new StringBufferCharList
042     * @throws IllegalArgumentException if the string is null
043     */
044    public static StringBufferCharList decorate(StringBuffer buf) {
045        if (buf == null) {
046            throw new IllegalArgumentException("StringBuffer must not be null");
047        }
048        return new StringBufferCharList(buf);
049    }
050    
051    //-----------------------------------------------------------------------
052    /**
053     * Constructor that uses an empty string as the datasource.
054     */
055    public StringBufferCharList() {
056        super();
057        this.stringBuffer = new StringBuffer();
058    }
059    
060    /**
061     * Constructor that copies the specified list.
062     * 
063     * @param list  the list to copy, must not be null
064     * @throws IllegalArgumentException if the list is null
065     */
066    public StringBufferCharList(CharList list) {
067        super();
068        if (list == null) {
069            throw new IllegalArgumentException("List must not be null");
070        }
071        this.stringBuffer = new StringBuffer(list.size());
072        this.stringBuffer.append(list.toCharArray());
073    }
074    
075    /**
076     * Constructor that copies the specified string.
077     * 
078     * @param str  the string to copy, must not be null
079     * @throws IllegalArgumentException if the string is null
080     */
081    public StringBufferCharList(String str) {
082        super();
083        if (str == null) {
084            throw new IllegalArgumentException("String must not be null");
085        }
086        this.stringBuffer = new StringBuffer(str);
087    }
088    
089    /**
090     * Constructor that <i>decorates</i> the specified string buffer.
091     * 
092     * @param buf  the string buffer to decorate, must not be null
093     * @throws IllegalArgumentException if the string is null
094     */
095    protected StringBufferCharList(StringBuffer buf) {
096        super();
097        if (buf == null) {
098            throw new IllegalArgumentException("StringBuffer must not be null");
099        }
100        this.stringBuffer = buf;
101    }
102    
103    // Implementation
104    //-----------------------------------------------------------------------
105    /**
106     * Gets the character at the specified index.
107     * 
108     * @param index  the index to retrieve
109     * @return the character at the specified index
110     */
111    public char getChar(int index) {
112        checkIndexExists(index);
113        return stringBuffer.charAt(index);
114    }
115
116    /**
117     * Gets the size of the list, which is the string length.
118     * 
119     * @return the string length
120     */
121    public int size() {
122        return stringBuffer.length();
123    }
124
125    /**
126     * Adds a primitive value to this list at an index.
127     *
128     * @param index  the index to add at
129     * @param value  the value to add to this collection
130     * @return <code>true</code> if this list was modified by this method call
131     * @throws IndexOutOfBoundsException if the index is invalid
132     */
133    public boolean add(int index, char value) {
134        checkAddModifiable();
135        checkIndex(index);
136        stringBuffer.insert(index, value);
137        return true;
138    }
139
140    /**
141     * Removes a primitive value by index from the list.
142     *
143     * @param index  the index to remove from
144     * @return the primitive value previously at this index
145     * @throws IndexOutOfBoundsException if the index is invalid
146     */
147    public char removeIndex(int index) {
148        checkRemoveModifiable();
149        checkIndexExists(index);
150        char old = stringBuffer.charAt(index);
151        stringBuffer.deleteCharAt(index);
152        return old;
153    }
154
155    /**
156     * Sets the primitive value at a specified index.
157     * <p>
158     * This implementation throws UnsupportedOperationException.
159     *
160     * @param index  the index to set
161     * @param value  the value to store
162     * @return the previous value at the index
163     * @throws IndexOutOfBoundsException if the index is invalid
164     */
165    public char set(int index, char value) {
166        checkSetModifiable();
167        checkIndexExists(index);
168        char old = stringBuffer.charAt(index);
169        stringBuffer.setCharAt(index, value);
170        return old;
171    }
172
173    /**
174     * Are the add methods supported.
175     *
176     * @return true
177     */
178    protected boolean isAddModifiable() {
179        return true;
180    }
181
182    /**
183     * Are the remove methods supported.
184     *
185     * @return true
186     */
187    protected boolean isRemoveModifiable() {
188        return true;
189    }
190
191    /**
192     * Are the set methods supported.
193     *
194     * @return true
195     */
196    protected boolean isSetModifiable() {
197        return true;
198    }
199
200    /**
201     * Is the collection modifiable in any way.
202     *
203     * @return true if supported
204     */
205    public boolean isModifiable() {
206        return true;
207    }
208
209    // Optimisation
210    //-----------------------------------------------------------------------
211    /**
212     * Adds a primitive value to this collection.
213     *
214     * @param value  the value to add to this collection
215     * @return <code>true</code> if this collection was modified by this method call
216     */
217    public boolean add(char value) {
218        checkAddModifiable();
219        stringBuffer.append(value);
220        return true;
221    }
222
223    /**
224     * Adds an array of primitive values to this list at an index.
225     *
226     * @param values  the values to add to this collection, null treated as empty array
227     * @return <code>true</code> if this list was modified by this method call
228     * @throws IndexOutOfBoundsException if the index is invalid
229     */
230    public boolean addAll(char[] values) {
231        checkAddModifiable();
232        if (values == null || values.length == 0) {
233            return false;
234        }
235        stringBuffer.append(values);
236        return true;
237    }
238
239    /**
240     * Adds an array of primitive values to this list at an index.
241     *
242     * @param index  the index to add at
243     * @param values  the values to add to this collection, null treated as empty array
244     * @return <code>true</code> if this list was modified by this method call
245     * @throws IndexOutOfBoundsException if the index is invalid
246     */
247    public boolean addAll(int index, char[] values) {
248        checkAddModifiable();
249        checkIndex(index);
250        if (values == null || values.length == 0) {
251            return false;
252        }
253        stringBuffer.insert(index, values);
254        return true;
255    }
256
257    /**
258     * Removes a range of values from the list.
259     *
260     * @param fromIndexInclusive  the start of the range to remove, inclusive
261     * @param toIndexExclusive  the end of the range to remove, exclusive
262     * @return <code>true</code> if the collection was modified
263     */
264    public boolean removeRange(int fromIndexInclusive, int toIndexExclusive) {
265        checkRemoveModifiable();
266        checkRange(fromIndexInclusive, toIndexExclusive);
267        if (fromIndexInclusive == toIndexExclusive) {
268            return false;
269        }
270        stringBuffer.delete(fromIndexInclusive, toIndexExclusive);
271        return true;
272    }
273
274    /**
275     * Gets the String underlying the list.
276     * 
277     * @return the underlying string, not null
278     */
279    public String toStringContents() {
280        return stringBuffer.substring(0, stringBuffer.length());
281    }
282
283    //-----------------------------------------------------------------------
284    /**
285     * Copies data from this collection into the specified array.
286     * This method is pre-validated.
287     * 
288     * @param fromIndex  the index to start from
289     * @param dest  the destination array
290     * @param destIndex  the destination start index
291     * @param size  the number of items to copy
292     */
293    protected void arrayCopy(int fromIndex, char[] dest, int destIndex, int size) {
294        stringBuffer.getChars(fromIndex, fromIndex + size, dest, destIndex);
295    }
296
297}