001    /**
002     * Copyright (c) 2000-present 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.memory.SoftReferenceThreadLocal;
018    
019    import java.io.IOException;
020    import java.io.Serializable;
021    import java.io.Writer;
022    
023    /**
024     * <p>
025     * See https://issues.liferay.com/browse/LPS-6072.
026     * </p>
027     *
028     * @author Shuyang Zhou
029     * @author Brian Wing Shun Chan
030     */
031    public class StringBundler implements Serializable {
032    
033            public StringBundler() {
034                    _array = new String[_DEFAULT_ARRAY_CAPACITY];
035            }
036    
037            public StringBundler(int initialCapacity) {
038                    if (initialCapacity <= 0) {
039                            initialCapacity = _DEFAULT_ARRAY_CAPACITY;
040                    }
041    
042                    _array = new String[initialCapacity];
043            }
044    
045            public StringBundler(String s) {
046                    _array = new String[_DEFAULT_ARRAY_CAPACITY];
047    
048                    _array[0] = s;
049    
050                    _arrayIndex = 1;
051            }
052    
053            public StringBundler(String[] stringArray) {
054                    this(stringArray, 0);
055            }
056    
057            public StringBundler(String[] stringArray, int extraSpace) {
058                    _array = new String[stringArray.length + extraSpace];
059    
060                    for (String s : stringArray) {
061                            if ((s != null) && (s.length() > 0)) {
062                                    _array[_arrayIndex++] = s;
063                            }
064                    }
065            }
066    
067            public StringBundler append(boolean b) {
068                    if (b) {
069                            return append(StringPool.TRUE);
070                    }
071                    else {
072                            return append(StringPool.FALSE);
073                    }
074            }
075    
076            public StringBundler append(char c) {
077                    return append(String.valueOf(c));
078            }
079    
080            public StringBundler append(char[] chars) {
081                    if (chars == null) {
082                            return append("null");
083                    }
084                    else {
085                            return append(new String(chars));
086                    }
087            }
088    
089            public StringBundler append(double d) {
090                    return append(Double.toString(d));
091            }
092    
093            public StringBundler append(float f) {
094                    return append(Float.toString(f));
095            }
096    
097            public StringBundler append(int i) {
098                    return append(Integer.toString(i));
099            }
100    
101            public StringBundler append(long l) {
102                    return append(Long.toString(l));
103            }
104    
105            public StringBundler append(Object obj) {
106                    return append(String.valueOf(obj));
107            }
108    
109            public StringBundler append(String s) {
110                    if (s == null) {
111                            s = StringPool.NULL;
112                    }
113    
114                    if (s.length() == 0) {
115                            return this;
116                    }
117    
118                    if (_arrayIndex >= _array.length) {
119                            expandCapacity(_array.length * 2);
120                    }
121    
122                    _array[_arrayIndex++] = s;
123    
124                    return this;
125            }
126    
127            public StringBundler append(String[] stringArray) {
128                    if (ArrayUtil.isEmpty(stringArray)) {
129                            return this;
130                    }
131    
132                    if ((_array.length - _arrayIndex) < stringArray.length) {
133                            expandCapacity((_array.length + stringArray.length) * 2);
134                    }
135    
136                    for (String s : stringArray) {
137                            if ((s != null) && (s.length() > 0)) {
138                                    _array[_arrayIndex++] = s;
139                            }
140                    }
141    
142                    return this;
143            }
144    
145            public StringBundler append(StringBundler sb) {
146                    if ((sb == null) || (sb._arrayIndex == 0)) {
147                            return this;
148                    }
149    
150                    if ((_array.length - _arrayIndex) < sb._arrayIndex) {
151                            expandCapacity((_array.length + sb._arrayIndex) * 2);
152                    }
153    
154                    System.arraycopy(sb._array, 0, _array, _arrayIndex, sb._arrayIndex);
155    
156                    _arrayIndex += sb._arrayIndex;
157    
158                    return this;
159            }
160    
161            public int capacity() {
162                    return _array.length;
163            }
164    
165            public String[] getStrings() {
166                    return _array;
167            }
168    
169            public int index() {
170                    return _arrayIndex;
171            }
172    
173            public int length() {
174                    int length = 0;
175    
176                    for (int i = 0; i < _arrayIndex; i++) {
177                            length += _array[i].length();
178                    }
179    
180                    return length;
181            }
182    
183            public void setIndex(int newIndex) {
184                    if (newIndex < 0) {
185                            throw new ArrayIndexOutOfBoundsException(newIndex);
186                    }
187    
188                    if (newIndex > _array.length) {
189                            String[] newArray = new String[newIndex];
190    
191                            System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
192    
193                            _array = newArray;
194                    }
195    
196                    if (_arrayIndex < newIndex) {
197                            for (int i = _arrayIndex; i < newIndex; i++) {
198                                    _array[i] = StringPool.BLANK;
199                            }
200                    }
201    
202                    if (_arrayIndex > newIndex) {
203                            for (int i = newIndex; i < _arrayIndex; i++) {
204                                    _array[i] = null;
205                            }
206                    }
207    
208                    _arrayIndex = newIndex;
209            }
210    
211            public void setStringAt(String s, int index) {
212                    if ((index < 0) || (index >= _arrayIndex)) {
213                            throw new ArrayIndexOutOfBoundsException(index);
214                    }
215    
216                    _array[index] = s;
217            }
218    
219            public String stringAt(int index) {
220                    if ((index < 0) || (index >= _arrayIndex)) {
221                            throw new ArrayIndexOutOfBoundsException(index);
222                    }
223    
224                    return _array[index];
225            }
226    
227            @Override
228            public String toString() {
229                    if (_arrayIndex == 0) {
230                            return StringPool.BLANK;
231                    }
232    
233                    if (_arrayIndex == 1) {
234                            return _array[0];
235                    }
236    
237                    if (_arrayIndex == 2) {
238                            return _array[0].concat(_array[1]);
239                    }
240    
241                    if (_arrayIndex == 3) {
242                            return _array[0].concat(_array[1]).concat(_array[2]);
243                    }
244    
245                    int length = 0;
246    
247                    for (int i = 0; i < _arrayIndex; i++) {
248                            length += _array[i].length();
249                    }
250    
251                    StringBuilder sb = null;
252    
253                    if (length > _THREAD_LOCAL_BUFFER_LIMIT) {
254                            sb = _stringBuilderThreadLocal.get();
255    
256                            if (sb == null) {
257                                    sb = new StringBuilder(length);
258    
259                                    _stringBuilderThreadLocal.set(sb);
260                            }
261                            else if (sb.capacity() < length) {
262                                    sb.setLength(length);
263                            }
264    
265                            sb.setLength(0);
266                    }
267                    else {
268                            sb = new StringBuilder(length);
269                    }
270    
271                    for (int i = 0; i < _arrayIndex; i++) {
272                            sb.append(_array[i]);
273                    }
274    
275                    return sb.toString();
276            }
277    
278            public void writeTo(Writer writer) throws IOException {
279                    for (int i = 0; i < _arrayIndex; i++) {
280                            writer.write(_array[i]);
281                    }
282            }
283    
284            protected void expandCapacity(int newCapacity) {
285                    String[] newArray = new String[newCapacity];
286    
287                    System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
288    
289                    _array = newArray;
290            }
291    
292            private static final int _DEFAULT_ARRAY_CAPACITY = 16;
293    
294            private static final int _THREAD_LOCAL_BUFFER_LIMIT;
295    
296            private static final ThreadLocal<StringBuilder> _stringBuilderThreadLocal;
297            private static final long serialVersionUID = 1L;
298    
299            static {
300                    int threadLocalBufferLimit = GetterUtil.getInteger(
301                            System.getProperty(
302                                    StringBundler.class.getName() + ".threadlocal.buffer.limit"),
303                            Integer.MAX_VALUE);
304    
305                    if ((threadLocalBufferLimit > 0) &&
306                            (threadLocalBufferLimit < Integer.MAX_VALUE)) {
307    
308                            _THREAD_LOCAL_BUFFER_LIMIT = threadLocalBufferLimit;
309    
310                            _stringBuilderThreadLocal = new SoftReferenceThreadLocal<>();
311                    }
312                    else {
313                            _THREAD_LOCAL_BUFFER_LIMIT = Integer.MAX_VALUE;
314    
315                            _stringBuilderThreadLocal = null;
316                    }
317            }
318    
319            private String[] _array;
320            private int _arrayIndex;
321    
322    }