001
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 import java.lang.reflect.Constructor;
024
025
034 public class StringBundler implements Serializable {
035
036 public StringBundler() {
037 _array = new String[_DEFAULT_ARRAY_CAPACITY];
038 }
039
040 public StringBundler(int initialCapacity) {
041 if (initialCapacity <= 0) {
042 initialCapacity = _DEFAULT_ARRAY_CAPACITY;
043 }
044
045 _array = new String[initialCapacity];
046 }
047
048 public StringBundler(String s) {
049 _array = new String[_DEFAULT_ARRAY_CAPACITY];
050
051 _array[0] = s;
052
053 _arrayIndex = 1;
054 }
055
056 public StringBundler(String[] stringArray) {
057 this(stringArray, 0);
058 }
059
060 public StringBundler(String[] stringArray, int extraSpace) {
061 _array = new String[stringArray.length + extraSpace];
062
063 for (int i = 0; i < stringArray.length; i++) {
064 String s = stringArray[i];
065
066 if ((s != null) && (s.length() > 0)) {
067 _array[_arrayIndex++] = s;
068 }
069 }
070 }
071
072 public StringBundler append(boolean b) {
073 if (b) {
074 return append(_TRUE);
075 }
076 else {
077 return append(_FALSE);
078 }
079 }
080
081 public StringBundler append(char c) {
082 return append(String.valueOf(c));
083 }
084
085 public StringBundler append(char[] chars) {
086 if (chars == null) {
087 return append("null");
088 }
089 else {
090 return append(new String(chars));
091 }
092 }
093
094 public StringBundler append(double d) {
095 return append(Double.toString(d));
096 }
097
098 public StringBundler append(float f) {
099 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 (ArrayUtil.isEmpty(stringArray)) {
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 @Override
231 public String toString() {
232 return toString(true);
233 }
234
235 public String toString(boolean unsafeCreate) {
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 if (_arrayIndex == 3) {
249 return _array[0].concat(_array[1]).concat(_array[2]);
250 }
251
252 int length = 0;
253
254 for (int i = 0; i < _arrayIndex; i++) {
255 length += _array[i].length();
256 }
257
258 UnsafeStringBuilder usb = null;
259
260 if ((length > _unsafeCreateLimit) && (_stringConstructor != null) &&
261 CharBufferPool.isEnabled() && unsafeCreate) {
262
263 char[] charBuffer = CharBufferPool.borrow(length);
264
265 int offset = 0;
266
267 for (int i = 0; i < _arrayIndex; i++) {
268 String s = _array[i];
269
270 s.getChars(0, s.length(), charBuffer, offset);
271
272 offset += s.length();
273 }
274
275 try {
276 return _stringConstructor.newInstance(0, length, charBuffer);
277 }
278 catch (Exception e) {
279 _stringConstructor = null;
280
281 return toString(false);
282 }
283 }
284 else if (length > _threadLocalBufferLimit) {
285 usb = _unsafeStringBuilderThreadLocal.get();
286
287 if (usb == null) {
288 usb = new UnsafeStringBuilder(length);
289
290 _unsafeStringBuilderThreadLocal.set(usb);
291 }
292 else {
293 usb.resetAndEnsureCapacity(length);
294 }
295 }
296 else {
297 usb = new UnsafeStringBuilder(length);
298 }
299
300 for (int i = 0; i < _arrayIndex; i++) {
301 usb.append(_array[i]);
302 }
303
304 return usb.toString();
305 }
306
307 public void writeTo(Writer writer) throws IOException {
308 for (int i = 0; i < _arrayIndex; i++) {
309 writer.write(_array[i]);
310 }
311 }
312
313 protected void expandCapacity(int newCapacity) {
314 String[] newArray = new String[newCapacity];
315
316 System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
317
318 _array = newArray;
319 }
320
321 private static final int _DEFAULT_ARRAY_CAPACITY = 16;
322
323 private static final String _FALSE = "false";
324
325 private static final int _THREADLOCAL_BUFFER_LIMIT = GetterUtil.getInteger(
326 System.getProperty(
327 StringBundler.class.getName() + ".threadlocal.buffer.limit"));
328
329 private static final String _TRUE = "true";
330
331 private static final int _UNSAFE_CREATE_LIMIT = GetterUtil.getInteger(
332 System.getProperty(
333 StringBundler.class.getName() + ".unsafe.create.limit"));
334
335 private static final long serialVersionUID = 1L;
336
337 private static final ThreadLocal<UnsafeStringBuilder>
338 _unsafeStringBuilderThreadLocal;
339 private static Constructor<String> _stringConstructor;
340 private static int _threadLocalBufferLimit;
341 private static int _unsafeCreateLimit;
342
343 static {
344 if (_THREADLOCAL_BUFFER_LIMIT > 0) {
345 _unsafeStringBuilderThreadLocal =
346 new SoftReferenceThreadLocal<UnsafeStringBuilder>();
347 _threadLocalBufferLimit = _THREADLOCAL_BUFFER_LIMIT;
348 }
349 else {
350 _unsafeStringBuilderThreadLocal = null;
351 _threadLocalBufferLimit = Integer.MAX_VALUE;
352 }
353
354 if (_UNSAFE_CREATE_LIMIT > 0) {
355 try {
356 _unsafeCreateLimit = _UNSAFE_CREATE_LIMIT;
357
358 _stringConstructor = String.class.getDeclaredConstructor(
359 int.class, int.class, char[].class);
360
361 _stringConstructor.setAccessible(true);
362 }
363 catch (Exception e) {
364 }
365 }
366 else {
367 _unsafeCreateLimit = Integer.MAX_VALUE;
368 _stringConstructor = null;
369 }
370 }
371
372 private String[] _array;
373 private int _arrayIndex;
374
375 private static class UnsafeStringBuilder {
376
377 public void append(String s) {
378 int length = s.length();
379
380 s.getChars(0, length, _value, _count);
381
382 _count += length;
383 }
384
385 public void resetAndEnsureCapacity(int newLength) {
386 if (_value.length < newLength) {
387 int length = _value.length * 2 + 2;
388
389 if (length < newLength) {
390 length = newLength;
391 }
392
393 _value = new char[length];
394 }
395
396 _count = 0;
397 }
398
399 @Override
400 public String toString() {
401 return new String(_value, 0, _count);
402 }
403
404 private UnsafeStringBuilder(int length) {
405 _value = new char[length];
406 }
407
408 private int _count;
409 private char[] _value;
410
411 }
412
413 }