1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.kernel.util;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  
20  import java.io.IOException;
21  import java.io.Writer;
22  
23  import java.lang.reflect.Constructor;
24  
25  /**
26   * <a href="StringBundler.java.html"><b><i>View Source</i></b></a>
27   *
28   * <p>
29   * See http://issues.liferay.com/browse/LPS-6072.
30   * </p>
31   *
32   * @author Shuyang Zhou
33   * @author Brian Wing Shun Chan
34   */
35  public class StringBundler {
36  
37      public static final int UNSAFE_CREATE_THRESHOLD = GetterUtil.getInteger(
38          System.getProperty(
39              StringBundler.class.getName() + ".unsafe.create.threshold"));
40  
41      public StringBundler() {
42          _array = new String[_DEFAULT_ARRAY_CAPACITY];
43      }
44  
45      public StringBundler(int initialCapacity) {
46          if (initialCapacity <= 0) {
47              throw new IllegalArgumentException();
48          }
49  
50          _array = new String[initialCapacity];
51      }
52  
53      public StringBundler(String s) {
54          _array = new String[_DEFAULT_ARRAY_CAPACITY];
55  
56          _array[0] = s;
57  
58          _arrayIndex = 1;
59      }
60  
61      public StringBundler(String[] stringArray) {
62          this(stringArray, 0);
63      }
64  
65      public StringBundler(String[] stringArray, int extraSpace) {
66          _array = new String[stringArray.length + extraSpace];
67  
68          for (int i = 0; i < stringArray.length; i++) {
69              String s = stringArray[i];
70  
71              if ((s != null) && (s.length() > 0)) {
72                  _array[_arrayIndex++] = s;
73              }
74          }
75      }
76  
77      public StringBundler append(boolean b) {
78          if (b) {
79              return append(_TRUE);
80          }
81          else {
82              return append(_FALSE);
83          }
84      }
85  
86      public StringBundler append(char c) {
87          return append(String.valueOf(c));
88      }
89  
90      public StringBundler append(char[] charArray) {
91          if (charArray == null) {
92              return append("null");
93          }
94          else {
95              return append(new String(charArray));
96          }
97      }
98  
99      public StringBundler append(double d) {
100         return append(Double.toString(d));
101     }
102 
103     public StringBundler append(float f) {
104         return append(Float.toString(f));
105     }
106 
107     public StringBundler append(int i) {
108         return append(Integer.toString(i));
109     }
110 
111     public StringBundler append(long l) {
112         return append(Long.toString(l));
113     }
114 
115     public StringBundler append(Object obj) {
116         return append(String.valueOf(obj));
117     }
118 
119     public StringBundler append(String s) {
120         if (s == null) {
121             s = StringPool.NULL;
122         }
123 
124         if (s.length() == 0) {
125             return this;
126         }
127 
128         if (_arrayIndex >= _array.length) {
129             expandCapacity(_array.length * 2);
130         }
131 
132         _array[_arrayIndex++] = s;
133 
134         return this;
135     }
136 
137     public StringBundler append(String[] stringArray) {
138         if ((stringArray == null) || (stringArray.length == 0)) {
139             return this;
140         }
141 
142         if ((_array.length - _arrayIndex) < stringArray.length) {
143             expandCapacity((_array.length + stringArray.length) * 2);
144         }
145 
146         for (int i = 0; i < stringArray.length; i++) {
147             String s = stringArray[i];
148 
149             if ((s != null) && (s.length() > 0)) {
150                 _array[_arrayIndex++] = s;
151             }
152         }
153 
154         return this;
155     }
156 
157     public StringBundler append(StringBundler sb) {
158         if ((sb == null) || (sb._arrayIndex == 0)) {
159             return this;
160         }
161 
162         if ((_array.length - _arrayIndex) < sb._arrayIndex) {
163             expandCapacity((_array.length + sb._arrayIndex) * 2);
164         }
165 
166         System.arraycopy(sb._array, 0, _array, _arrayIndex, sb._arrayIndex);
167 
168         _arrayIndex += sb._arrayIndex;
169 
170         return this;
171     }
172 
173     public int capacity() {
174         return _array.length;
175     }
176 
177     public int index() {
178         return _arrayIndex;
179     }
180 
181     public int length() {
182         int length = 0;
183 
184         for (int i = 0; i < _arrayIndex; i++) {
185             length += _array[i].length();
186         }
187 
188         return length;
189     }
190 
191     public void setIndex(int newIndex) {
192         if (newIndex < 0) {
193             throw new ArrayIndexOutOfBoundsException(newIndex);
194         }
195 
196         if (newIndex > _array.length) {
197             String[] newArray = new String[newIndex];
198 
199             System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
200 
201             _array = newArray;
202         }
203 
204         if (_arrayIndex < newIndex) {
205             for (int i = _arrayIndex; i < newIndex; i++) {
206                 _array[i] = StringPool.BLANK;
207             }
208         }
209 
210         if (_arrayIndex > newIndex) {
211             for (int i = newIndex; i < _arrayIndex; i++) {
212                 _array[i] = null;
213             }
214         }
215 
216         _arrayIndex = newIndex;
217     }
218 
219     public void setStringAt(String s, int index) {
220         if ((index < 0) || (index >= _arrayIndex)) {
221             throw new ArrayIndexOutOfBoundsException(index);
222         }
223 
224         _array[index] = s;
225     }
226 
227     public String stringAt(int index) {
228         if ((index < 0) || (index >= _arrayIndex)) {
229             throw new ArrayIndexOutOfBoundsException(index);
230         }
231 
232         return _array[index];
233     }
234 
235     public String toString() {
236         if (_arrayIndex == 0) {
237             return StringPool.BLANK;
238         }
239 
240         if (_arrayIndex == 1) {
241             return _array[0];
242         }
243 
244         if (_arrayIndex == 2) {
245             return _array[0].concat(_array[1]);
246         }
247 
248         int length = 0;
249 
250         for (int i = 0; i < _arrayIndex; i++) {
251             length += _array[i].length();
252         }
253 
254         if ((_unsafeStringConstructor != null) &&
255             (length >= UNSAFE_CREATE_THRESHOLD)) {
256 
257             return unsafeCreate(_array, _arrayIndex, length);
258         }
259         else if (_arrayIndex == 3) {
260             return _array[0].concat(_array[1]).concat(_array[2]);
261         }
262         else {
263             return safeCreate(_array, _arrayIndex, length);
264         }
265     }
266 
267     public void writeTo(Writer writer) throws IOException {
268         for (int i = 0; i < _arrayIndex; i++) {
269             writer.write(_array[i]);
270         }
271     }
272 
273     protected void expandCapacity(int newCapacity) {
274         String[] newArray = new String[newCapacity];
275 
276         System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
277 
278         _array = newArray;
279     }
280 
281     protected String safeCreate(String[] array, int index, int length) {
282         StringBuilder sb = new StringBuilder(length);
283 
284         for (int i = 0; i < index; i++) {
285             sb.append(array[i]);
286         }
287 
288         return sb.toString();
289     }
290 
291     protected String unsafeCreate(String[] array, int index, int length) {
292         char[] charArray = new char[length];
293 
294         int offset = 0;
295 
296         for (int i = 0; i < index; i++) {
297             String s = array[i];
298 
299             s.getChars(0, s.length(), charArray, offset);
300 
301             offset += s.length();
302         }
303 
304         try {
305             return _unsafeStringConstructor.newInstance(0, length, charArray);
306         }
307         catch (Exception e) {
308             throw new IllegalStateException(e);
309         }
310     }
311 
312     private static final int _DEFAULT_ARRAY_CAPACITY = 16;
313 
314     private static final String _FALSE = "false";
315 
316     private static final String _TRUE = "true";
317 
318     private static Log _log = LogFactoryUtil.getLog(StringBundler.class);
319 
320     private static Constructor<String> _unsafeStringConstructor;
321 
322     static {
323         if (UNSAFE_CREATE_THRESHOLD > 0) {
324             try {
325                 _unsafeStringConstructor = String.class.getDeclaredConstructor(
326                     int.class, int.class, char[].class);
327 
328                 _unsafeStringConstructor.setAccessible(true);
329             }
330             catch (Exception e) {
331                 _log.error(e, e);
332             }
333         }
334     }
335 
336     private String[] _array;
337     private int _arrayIndex;
338 
339 }