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     */
016    package org.joda.primitives.list.impl;
017    
018    import 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     */
029    public 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    }