1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.kernel.util;
16  
17  import com.liferay.portal.kernel.memory.SoftReferenceThreadLocal;
18  
19  import java.io.IOException;
20  import java.io.Writer;
21  
22  import java.lang.reflect.Constructor;
23  
24  /**
25   * <a href="StringBundler.java.html"><b><i>View Source</i></b></a>
26   *
27   * <p>
28   * See http://issues.liferay.com/browse/LPS-6072.
29   * </p>
30   *
31   * @author Shuyang Zhou
32   * @author Brian Wing Shun Chan
33   */
34  public class StringBundler {
35  
36      public StringBundler() {
37          _array = new String[_DEFAULT_ARRAY_CAPACITY];
38      }
39  
40      public StringBundler(int initialCapacity) {
41          if (initialCapacity <= 0) {
42              throw new IllegalArgumentException();
43          }
44  
45          _array = new String[initialCapacity];
46      }
47  
48      public StringBundler(String s) {
49          _array = new String[_DEFAULT_ARRAY_CAPACITY];
50  
51          _array[0] = s;
52  
53          _arrayIndex = 1;
54      }
55  
56      public StringBundler(String[] stringArray) {
57          this(stringArray, 0);
58      }
59  
60      public StringBundler(String[] stringArray, int extraSpace) {
61          _array = new String[stringArray.length + extraSpace];
62  
63          for (int i = 0; i < stringArray.length; i++) {
64              String s = stringArray[i];
65  
66              if ((s != null) && (s.length() > 0)) {
67                  _array[_arrayIndex++] = s;
68              }
69          }
70      }
71  
72      public StringBundler append(boolean b) {
73          if (b) {
74              return append(_TRUE);
75          }
76          else {
77              return append(_FALSE);
78          }
79      }
80  
81      public StringBundler append(char c) {
82          return append(String.valueOf(c));
83      }
84  
85      public StringBundler append(char[] charArray) {
86          if (charArray == null) {
87              return append("null");
88          }
89          else {
90              return append(new String(charArray));
91          }
92      }
93  
94      public StringBundler append(double d) {
95          return append(Double.toString(d));
96      }
97  
98      public StringBundler append(float f) {
99          return append(Float.toString(f));
100     }
101 
102     public StringBundler append(int i) {
103         return append(Integer.toString(i));
104     }
105 
106     public StringBundler append(long l) {
107         return append(Long.toString(l));
108     }
109 
110     public StringBundler append(Object obj) {
111         return append(String.valueOf(obj));
112     }
113 
114     public StringBundler append(String s) {
115         if (s == null) {
116             s = StringPool.NULL;
117         }
118 
119         if (s.length() == 0) {
120             return this;
121         }
122 
123         if (_arrayIndex >= _array.length) {
124             expandCapacity(_array.length * 2);
125         }
126 
127         _array[_arrayIndex++] = s;
128 
129         return this;
130     }
131 
132     public StringBundler append(String[] stringArray) {
133         if ((stringArray == null) || (stringArray.length == 0)) {
134             return this;
135         }
136 
137         if ((_array.length - _arrayIndex) < stringArray.length) {
138             expandCapacity((_array.length + stringArray.length) * 2);
139         }
140 
141         for (int i = 0; i < stringArray.length; i++) {
142             String s = stringArray[i];
143 
144             if ((s != null) && (s.length() > 0)) {
145                 _array[_arrayIndex++] = s;
146             }
147         }
148 
149         return this;
150     }
151 
152     public StringBundler append(StringBundler sb) {
153         if ((sb == null) || (sb._arrayIndex == 0)) {
154             return this;
155         }
156 
157         if ((_array.length - _arrayIndex) < sb._arrayIndex) {
158             expandCapacity((_array.length + sb._arrayIndex) * 2);
159         }
160 
161         System.arraycopy(sb._array, 0, _array, _arrayIndex, sb._arrayIndex);
162 
163         _arrayIndex += sb._arrayIndex;
164 
165         return this;
166     }
167 
168     public int capacity() {
169         return _array.length;
170     }
171 
172     public int index() {
173         return _arrayIndex;
174     }
175 
176     public int length() {
177         int length = 0;
178 
179         for (int i = 0; i < _arrayIndex; i++) {
180             length += _array[i].length();
181         }
182 
183         return length;
184     }
185 
186     public void setIndex(int newIndex) {
187         if (newIndex < 0) {
188             throw new ArrayIndexOutOfBoundsException(newIndex);
189         }
190 
191         if (newIndex > _array.length) {
192             String[] newArray = new String[newIndex];
193 
194             System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
195 
196             _array = newArray;
197         }
198 
199         if (_arrayIndex < newIndex) {
200             for (int i = _arrayIndex; i < newIndex; i++) {
201                 _array[i] = StringPool.BLANK;
202             }
203         }
204 
205         if (_arrayIndex > newIndex) {
206             for (int i = newIndex; i < _arrayIndex; i++) {
207                 _array[i] = null;
208             }
209         }
210 
211         _arrayIndex = newIndex;
212     }
213 
214     public void setStringAt(String s, int index) {
215         if ((index < 0) || (index >= _arrayIndex)) {
216             throw new ArrayIndexOutOfBoundsException(index);
217         }
218 
219         _array[index] = s;
220     }
221 
222     public String stringAt(int index) {
223         if ((index < 0) || (index >= _arrayIndex)) {
224             throw new ArrayIndexOutOfBoundsException(index);
225         }
226 
227         return _array[index];
228     }
229 
230     public String toString() {
231         return toString(true);
232     }
233 
234     public String toString(boolean unsafeCreate) {
235         if (_arrayIndex == 0) {
236             return StringPool.BLANK;
237         }
238 
239         if (_arrayIndex == 1) {
240             return _array[0];
241         }
242 
243         if (_arrayIndex == 2) {
244             return _array[0].concat(_array[1]);
245         }
246 
247         if (_arrayIndex == 3) {
248             return _array[0].concat(_array[1]).concat(_array[2]);
249         }
250 
251         int length = 0;
252 
253         for (int i = 0; i < _arrayIndex; i++) {
254             length += _array[i].length();
255         }
256 
257         StringBuilder sb = null;
258 
259         if ((length > _unsafeCreateLimit) && (_stringConstructor != null) &&
260             CharBufferPool.isEnabled() && unsafeCreate) {
261 
262             char[] charBuffer = CharBufferPool.borrow(length);
263 
264             int offset = 0;
265 
266             for (int i = 0; i < _arrayIndex; i++) {
267                 String s = _array[i];
268 
269                 s.getChars(0, s.length(), charBuffer, offset);
270 
271                 offset += s.length();
272             }
273 
274             try {
275                 return _stringConstructor.newInstance(0, length, charBuffer);
276             }
277             catch (Exception e) {
278                 _stringConstructor = null;
279 
280                 return toString(false);
281             }
282         }
283         else if (length > _threadLocalBufferLimit) {
284             sb = _stringBuilderThreadLocal.get();
285 
286             if (sb == null) {
287                 sb = new StringBuilder(length);
288 
289                 _stringBuilderThreadLocal.set(sb);
290             }
291             else if (sb.capacity() < length) {
292                 sb.setLength(length);
293             }
294 
295             sb.setLength(0);
296         }
297         else {
298             sb = new StringBuilder(length);
299         }
300 
301         for (int i = 0; i < _arrayIndex; i++) {
302             sb.append(_array[i]);
303         }
304 
305         return sb.toString();
306     }
307 
308     public void writeTo(Writer writer) throws IOException {
309         for (int i = 0; i < _arrayIndex; i++) {
310             writer.write(_array[i]);
311         }
312     }
313 
314     protected void expandCapacity(int newCapacity) {
315         String[] newArray = new String[newCapacity];
316 
317         System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
318 
319         _array = newArray;
320     }
321 
322     private static final int _DEFAULT_ARRAY_CAPACITY = 16;
323 
324     private static final String _FALSE = "false";
325 
326     private static final int _THREADLOCAL_BUFFER_LIMIT = GetterUtil.getInteger(
327         System.getProperty(
328             StringBundler.class.getName() + ".threadlocal.buffer.limit"));
329 
330     private static final String _TRUE = "true";
331 
332     private static final int _UNSAFE_CREATE_LIMIT = GetterUtil.getInteger(
333         System.getProperty(
334             StringBundler.class.getName() + ".unsafe.create.limit"));
335 
336     private static ThreadLocal<StringBuilder> _stringBuilderThreadLocal;
337     private static int _threadLocalBufferLimit;
338     private static int _unsafeCreateLimit;
339     private static Constructor<String> _stringConstructor;
340 
341     static {
342         if (_THREADLOCAL_BUFFER_LIMIT > 0) {
343             _stringBuilderThreadLocal =
344                 new SoftReferenceThreadLocal<StringBuilder>();
345             _threadLocalBufferLimit = _THREADLOCAL_BUFFER_LIMIT;
346         }
347         else {
348             _stringBuilderThreadLocal = null;
349             _threadLocalBufferLimit = Integer.MAX_VALUE;
350         }
351 
352         if (_UNSAFE_CREATE_LIMIT > 0) {
353             try {
354                 _unsafeCreateLimit = _UNSAFE_CREATE_LIMIT;
355 
356                 _stringConstructor = String.class.getDeclaredConstructor(
357                     int.class, int.class, char[].class);
358 
359                 _stringConstructor.setAccessible(true);
360             }
361             catch (Exception e) {
362             }
363         }
364         else {
365             _unsafeCreateLimit = Integer.MAX_VALUE;
366             _stringConstructor = null;
367         }
368     }
369 
370     private String[] _array;
371     private int _arrayIndex;
372 
373 }