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.wicket.util.string;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.StringTokenizer;
025
026/**
027 * A typesafe, mutable list of strings supporting a variety of convenient operations as well as
028 * expected operations from List such as add(), size(), remove(), iterator(), get(int index) and
029 * toArray(). Instances of the class are not threadsafe.
030 * <p>
031 * StringList objects can be constructed empty or they can be created using any of several static
032 * factory methods:
033 * <ul>
034 * <li>valueOf(String[])
035 * <li>valueOf(String)
036 * <li>valueOf(Collection)
037 * <li>valueOf(Object[])
038 * </ul>
039 * In the case of the Collection and Object[] factory methods, each Object in the collection or
040 * array is converted to a String via toString() before being added to the StringList.
041 * <p>
042 * The tokenize() factory methods allow easy creation of StringLists via StringTokenizer. The
043 * repeat() static factory method creates a StringList that repeats a given String a given number of
044 * times.
045 * <p>
046 * The prepend() method adds a String to the beginning of the StringList. The removeLast() method
047 * pops a String off the end of the list. The sort() method sorts strings in the List using
048 * Collections.sort(). The class also inherits useful methods from AbstractStringList that include
049 * join() methods ala Perl and a toString() method which joins the list of strings with comma
050 * separators for easy viewing.
051 * 
052 * @author Jonathan Locke
053 */
054public final class StringList extends AbstractStringList
055{
056        private static final long serialVersionUID = 1L;
057
058        // The underlying list of strings
059        private final List<String> strings;
060
061        // The total length of all strings in the list
062        private int totalLength;
063
064        /**
065         * Returns a list of a string repeated a given number of times.
066         * 
067         * @param count
068         *            The number of times to repeat the string
069         * @param string
070         *            The string to repeat
071         * @return The list of strings
072         */
073        public static StringList repeat(final int count, final String string)
074        {
075                final StringList list = new StringList(count);
076
077                for (int i = 0; i < count; i++)
078                {
079                        list.add(string);
080                }
081
082                return list;
083        }
084
085        /**
086         * Extracts tokens from a comma and space delimited string.
087         * 
088         * @param string
089         *            The string
090         * @return The string tokens as a list
091         */
092        public static StringList tokenize(final String string)
093        {
094                return tokenize(string, ", ");
095        }
096
097        /**
098         * Extracts tokens from a delimited string.
099         * 
100         * @param string
101         *            The string
102         * @param delimiters
103         *            The delimiters
104         * @return The string tokens as a list
105         */
106        public static StringList tokenize(final String string, final String delimiters)
107        {
108                final StringTokenizer tokenizer = new StringTokenizer(string, delimiters);
109                final StringList strings = new StringList();
110
111                while (tokenizer.hasMoreTokens())
112                {
113                        strings.add(tokenizer.nextToken());
114                }
115
116                return strings;
117        }
118
119        /**
120         * Converts a collection of objects into a list of string values by using the conversion methods
121         * of the StringValue class.
122         * 
123         * @param collection
124         *            The collection to add as strings
125         * @return The list
126         */
127        public static StringList valueOf(final Collection<?> collection)
128        {
129                if (collection != null)
130                {
131                        final StringList strings = new StringList(collection.size());
132
133                        for (Object object : collection)
134                        {
135                                strings.add(StringValue.valueOf(object));
136                        }
137
138                        return strings;
139                }
140                else
141                {
142                        return new StringList();
143                }
144        }
145
146        /**
147         * Converts an array of objects into a list of strings by using the object to string conversion
148         * method of the StringValue class.
149         * 
150         * @param objects
151         *            The objects to convert
152         * @return The list of strings
153         */
154        public static StringList valueOf(final Object[] objects)
155        {
156                // check for null parameter
157                int length = (objects == null) ? 0 : objects.length;
158                final StringList strings = new StringList(length);
159
160                for (int i = 0; i < length; i++)
161                {
162                        strings.add(StringValue.valueOf(objects[i]));
163                }
164
165                return strings;
166        }
167
168        /**
169         * Returns a string list with just one string in it.
170         * 
171         * @param string
172         *            The string
173         * @return The list of one string
174         */
175        public static StringList valueOf(final String string)
176        {
177                final StringList strings = new StringList();
178
179                if (string != null)
180                {
181                        strings.add(string);
182                }
183
184                return strings;
185        }
186
187        /**
188         * Converts a string array to a string list.
189         * 
190         * @param array
191         *            The array
192         * @return The list
193         */
194        public static StringList valueOf(final String[] array)
195        {
196                int length = (array == null) ? 0 : array.length;
197                final StringList strings = new StringList(length);
198
199                for (int i = 0; i < length; i++)
200                {
201                        strings.add(array[i]);
202                }
203
204                return strings;
205        }
206
207        /**
208         * Constructor.
209         */
210        public StringList()
211        {
212                strings = new ArrayList<>();
213        }
214
215        /**
216         * Constructor.
217         * 
218         * @param size
219         *            Number of elements to preallocate
220         */
221        public StringList(final int size)
222        {
223                strings = new ArrayList<>(size);
224        }
225
226        /**
227         * Adds a string to the back of this list.
228         * 
229         * @param string
230         *            String to add
231         */
232        public void add(final String string)
233        {
234                // Add to list
235                add(size(), string);
236        }
237
238        /**
239         * Adds the string to the stringlist at position pos.
240         * 
241         * @param pos
242         *            the position to add the string at
243         * @param string
244         *            the string to add.
245         */
246        public void add(final int pos, final String string)
247        {
248                strings.add(pos, string == null ? "" : string);
249
250                // Increase total length
251                totalLength += string == null ? 0 : string.length();
252        }
253
254        /**
255         * Adds a string value to this list as a string.
256         * 
257         * @param value
258         *            The value to add
259         */
260        public void add(final StringValue value)
261        {
262                add(value.toString());
263        }
264
265        /**
266         * @param string
267         *            The string to look for
268         * @return True if the list contains the string
269         */
270        public boolean contains(final String string)
271        {
272                return strings.contains(string);
273        }
274
275        /**
276         * Gets the string at the given index.
277         * 
278         * @param index
279         *            The index
280         * @return The string at the index
281         * @throws IndexOutOfBoundsException
282         */
283        @Override
284        public String get(final int index)
285        {
286                return strings.get(index);
287        }
288
289        /**
290         * @return List value (not a copy of this list)
291         */
292        public List<String> getList()
293        {
294                return strings;
295        }
296
297        /**
298         * Returns a typesafe iterator over this collection of strings.
299         * 
300         * @return Typesafe string iterator
301         */
302        @Override
303        public IStringIterator iterator()
304        {
305                return new IStringIterator()
306                {
307                        private final Iterator<String> iterator = strings.iterator();
308
309                        @Override
310                        public boolean hasNext()
311                        {
312                                return iterator.hasNext();
313                        }
314
315                        @Override
316                        public String next()
317                        {
318                                return iterator.next();
319                        }
320                };
321        }
322
323        /**
324         * Adds the given string to the front of the list.
325         * 
326         * @param string
327         *            The string to add
328         */
329        public void prepend(final String string)
330        {
331                add(0, string);
332        }
333
334        /**
335         * Removes the string at the given index.
336         * 
337         * @param index
338         *            The index
339         */
340        public void remove(final int index)
341        {
342                String string = strings.remove(index);
343                totalLength = totalLength - string.length();
344        }
345
346        /**
347         * Removes the last string in this list.
348         */
349        public void removeLast()
350        {
351                remove(size() - 1);
352        }
353
354        /**
355         * @return The number of strings in this list.
356         */
357        @Override
358        public int size()
359        {
360                return strings.size();
361        }
362
363        /**
364         * Sorts this string list alphabetically.
365         */
366        public void sort()
367        {
368                Collections.sort(strings);
369        }
370
371        /**
372         * Converts this string list to a string array.
373         * 
374         * @return The string array
375         */
376        @Override
377        public String[] toArray()
378        {
379                return strings.toArray(new String[0]);
380        }
381
382        /**
383         * @return The total length of all strings in this list.
384         */
385        @Override
386        public int totalLength()
387        {
388                return totalLength;
389        }
390}