1
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
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 }