001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    
020    import java.io.IOException;
021    import java.io.Writer;
022    
023    import java.lang.reflect.Constructor;
024    
025    /**
026     * <p>
027     * See http://issues.liferay.com/browse/LPS-6072.
028     * </p>
029     *
030     * @author Shuyang Zhou
031     * @author Brian Wing Shun Chan
032     */
033    public class StringBundler {
034    
035            public static final int UNSAFE_CREATE_THRESHOLD = GetterUtil.getInteger(
036                    System.getProperty(
037                            StringBundler.class.getName() + ".unsafe.create.threshold"));
038    
039            public StringBundler() {
040                    _array = new String[_DEFAULT_ARRAY_CAPACITY];
041            }
042    
043            public StringBundler(int initialCapacity) {
044                    if (initialCapacity <= 0) {
045                            throw new IllegalArgumentException();
046                    }
047    
048                    _array = new String[initialCapacity];
049            }
050    
051            public StringBundler(String s) {
052                    _array = new String[_DEFAULT_ARRAY_CAPACITY];
053    
054                    _array[0] = s;
055    
056                    _arrayIndex = 1;
057            }
058    
059            public StringBundler(String[] stringArray) {
060                    this(stringArray, 0);
061            }
062    
063            public StringBundler(String[] stringArray, int extraSpace) {
064                    _array = new String[stringArray.length + extraSpace];
065    
066                    for (int i = 0; i < stringArray.length; i++) {
067                            String s = stringArray[i];
068    
069                            if ((s != null) && (s.length() > 0)) {
070                                    _array[_arrayIndex++] = s;
071                            }
072                    }
073            }
074    
075            public StringBundler append(boolean b) {
076                    if (b) {
077                            return append(_TRUE);
078                    }
079                    else {
080                            return append(_FALSE);
081                    }
082            }
083    
084            public StringBundler append(char c) {
085                    return append(String.valueOf(c));
086            }
087    
088            public StringBundler append(char[] charArray) {
089                    if (charArray == null) {
090                            return append("null");
091                    }
092                    else {
093                            return append(new String(charArray));
094                    }
095            }
096    
097            public StringBundler append(double d) {
098                    return append(Double.toString(d));
099            }
100    
101            public StringBundler append(float f) {
102                    return append(Float.toString(f));
103            }
104    
105            public StringBundler append(int i) {
106                    return append(Integer.toString(i));
107            }
108    
109            public StringBundler append(long l) {
110                    return append(Long.toString(l));
111            }
112    
113            public StringBundler append(Object obj) {
114                    return append(String.valueOf(obj));
115            }
116    
117            public StringBundler append(String s) {
118                    if (s == null) {
119                            s = StringPool.NULL;
120                    }
121    
122                    if (s.length() == 0) {
123                            return this;
124                    }
125    
126                    if (_arrayIndex >= _array.length) {
127                            expandCapacity(_array.length * 2);
128                    }
129    
130                    _array[_arrayIndex++] = s;
131    
132                    return this;
133            }
134    
135            public StringBundler append(String[] stringArray) {
136                    if ((stringArray == null) || (stringArray.length == 0)) {
137                            return this;
138                    }
139    
140                    if ((_array.length - _arrayIndex) < stringArray.length) {
141                            expandCapacity((_array.length + stringArray.length) * 2);
142                    }
143    
144                    for (int i = 0; i < stringArray.length; i++) {
145                            String s = stringArray[i];
146    
147                            if ((s != null) && (s.length() > 0)) {
148                                    _array[_arrayIndex++] = s;
149                            }
150                    }
151    
152                    return this;
153            }
154    
155            public StringBundler append(StringBundler sb) {
156                    if ((sb == null) || (sb._arrayIndex == 0)) {
157                            return this;
158                    }
159    
160                    if ((_array.length - _arrayIndex) < sb._arrayIndex) {
161                            expandCapacity((_array.length + sb._arrayIndex) * 2);
162                    }
163    
164                    System.arraycopy(sb._array, 0, _array, _arrayIndex, sb._arrayIndex);
165    
166                    _arrayIndex += sb._arrayIndex;
167    
168                    return this;
169            }
170    
171            public int capacity() {
172                    return _array.length;
173            }
174    
175            public int index() {
176                    return _arrayIndex;
177            }
178    
179            public int length() {
180                    int length = 0;
181    
182                    for (int i = 0; i < _arrayIndex; i++) {
183                            length += _array[i].length();
184                    }
185    
186                    return length;
187            }
188    
189            public void setIndex(int newIndex) {
190                    if (newIndex < 0) {
191                            throw new ArrayIndexOutOfBoundsException(newIndex);
192                    }
193    
194                    if (newIndex > _array.length) {
195                            String[] newArray = new String[newIndex];
196    
197                            System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
198    
199                            _array = newArray;
200                    }
201    
202                    if (_arrayIndex < newIndex) {
203                            for (int i = _arrayIndex; i < newIndex; i++) {
204                                    _array[i] = StringPool.BLANK;
205                            }
206                    }
207    
208                    if (_arrayIndex > newIndex) {
209                            for (int i = newIndex; i < _arrayIndex; i++) {
210                                    _array[i] = null;
211                            }
212                    }
213    
214                    _arrayIndex = newIndex;
215            }
216    
217            public void setStringAt(String s, int index) {
218                    if ((index < 0) || (index >= _arrayIndex)) {
219                            throw new ArrayIndexOutOfBoundsException(index);
220                    }
221    
222                    _array[index] = s;
223            }
224    
225            public String stringAt(int index) {
226                    if ((index < 0) || (index >= _arrayIndex)) {
227                            throw new ArrayIndexOutOfBoundsException(index);
228                    }
229    
230                    return _array[index];
231            }
232    
233            public String toString() {
234                    if (_arrayIndex == 0) {
235                            return StringPool.BLANK;
236                    }
237    
238                    if (_arrayIndex == 1) {
239                            return _array[0];
240                    }
241    
242                    if (_arrayIndex == 2) {
243                            return _array[0].concat(_array[1]);
244                    }
245    
246                    int length = 0;
247    
248                    for (int i = 0; i < _arrayIndex; i++) {
249                            length += _array[i].length();
250                    }
251    
252                    if ((_unsafeStringConstructor != null) &&
253                            (length >= UNSAFE_CREATE_THRESHOLD)) {
254    
255                            return unsafeCreate(_array, _arrayIndex, length);
256                    }
257                    else if (_arrayIndex == 3) {
258                            return _array[0].concat(_array[1]).concat(_array[2]);
259                    }
260                    else {
261                            return safeCreate(_array, _arrayIndex, length);
262                    }
263            }
264    
265            public void writeTo(Writer writer) throws IOException {
266                    for (int i = 0; i < _arrayIndex; i++) {
267                            writer.write(_array[i]);
268                    }
269            }
270    
271            protected void expandCapacity(int newCapacity) {
272                    String[] newArray = new String[newCapacity];
273    
274                    System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
275    
276                    _array = newArray;
277            }
278    
279            protected String safeCreate(String[] array, int index, int length) {
280                    StringBuilder sb = new StringBuilder(length);
281    
282                    for (int i = 0; i < index; i++) {
283                            sb.append(array[i]);
284                    }
285    
286                    return sb.toString();
287            }
288    
289            protected String unsafeCreate(String[] array, int index, int length) {
290                    char[] charArray = new char[length];
291    
292                    int offset = 0;
293    
294                    for (int i = 0; i < index; i++) {
295                            String s = array[i];
296    
297                            s.getChars(0, s.length(), charArray, offset);
298    
299                            offset += s.length();
300                    }
301    
302                    try {
303                            return _unsafeStringConstructor.newInstance(0, length, charArray);
304                    }
305                    catch (Exception e) {
306                            throw new IllegalStateException(e);
307                    }
308            }
309    
310            private static final int _DEFAULT_ARRAY_CAPACITY = 16;
311    
312            private static final String _FALSE = "false";
313    
314            private static final String _TRUE = "true";
315    
316            private static Log _log = LogFactoryUtil.getLog(StringBundler.class);
317    
318            private static Constructor<String> _unsafeStringConstructor;
319    
320            static {
321                    if (UNSAFE_CREATE_THRESHOLD > 0) {
322                            try {
323                                    _unsafeStringConstructor = String.class.getDeclaredConstructor(
324                                            int.class, int.class, char[].class);
325    
326                                    _unsafeStringConstructor.setAccessible(true);
327                            }
328                            catch (Exception e) {
329                                    _log.error(e, e);
330                            }
331                    }
332            }
333    
334            private String[] _array;
335            private int _arrayIndex;
336    
337    }