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
033 public class StringBundler implements Serializable {
034
035 public StringBundler() {
036 _array = new String[_DEFAULT_ARRAY_CAPACITY];
037 }
038
039 public StringBundler(int initialCapacity) {
040 if (initialCapacity <= 0) {
041 initialCapacity = _DEFAULT_ARRAY_CAPACITY;
042 }
043
044 _array = new String[initialCapacity];
045 }
046
047 public StringBundler(String s) {
048 _array = new String[_DEFAULT_ARRAY_CAPACITY];
049
050 _array[0] = s;
051
052 _arrayIndex = 1;
053 }
054
055 public StringBundler(String[] stringArray) {
056 this(stringArray, 0);
057 }
058
059 public StringBundler(String[] stringArray, int extraSpace) {
060 _array = new String[stringArray.length + extraSpace];
061
062 for (int i = 0; i < stringArray.length; i++) {
063 String s = stringArray[i];
064
065 if ((s != null) && (s.length() > 0)) {
066 _array[_arrayIndex++] = s;
067 }
068 }
069 }
070
071 public StringBundler append(boolean b) {
072 if (b) {
073 return append(_TRUE);
074 }
075 else {
076 return append(_FALSE);
077 }
078 }
079
080 public StringBundler append(char c) {
081 return append(String.valueOf(c));
082 }
083
084 public StringBundler append(char[] chars) {
085 if (chars == null) {
086 return append("null");
087 }
088 else {
089 return append(new String(chars));
090 }
091 }
092
093 public StringBundler append(double d) {
094 return append(Double.toString(d));
095 }
096
097 public StringBundler append(float f) {
098 return append(Float.toString(f));
099 }
100
101 public StringBundler append(int i) {
102 return append(Integer.toString(i));
103 }
104
105 public StringBundler append(long l) {
106 return append(Long.toString(l));
107 }
108
109 public StringBundler append(Object obj) {
110 return append(String.valueOf(obj));
111 }
112
113 public StringBundler append(String s) {
114 if (s == null) {
115 s = StringPool.NULL;
116 }
117
118 if (s.length() == 0) {
119 return this;
120 }
121
122 if (_arrayIndex >= _array.length) {
123 expandCapacity(_array.length * 2);
124 }
125
126 _array[_arrayIndex++] = s;
127
128 return this;
129 }
130
131 public StringBundler append(String[] stringArray) {
132 if (ArrayUtil.isEmpty(stringArray)) {
133 return this;
134 }
135
136 if ((_array.length - _arrayIndex) < stringArray.length) {
137 expandCapacity((_array.length + stringArray.length) * 2);
138 }
139
140 for (int i = 0; i < stringArray.length; i++) {
141 String s = stringArray[i];
142
143 if ((s != null) && (s.length() > 0)) {
144 _array[_arrayIndex++] = s;
145 }
146 }
147
148 return this;
149 }
150
151 public StringBundler append(StringBundler sb) {
152 if ((sb == null) || (sb._arrayIndex == 0)) {
153 return this;
154 }
155
156 if ((_array.length - _arrayIndex) < sb._arrayIndex) {
157 expandCapacity((_array.length + sb._arrayIndex) * 2);
158 }
159
160 System.arraycopy(sb._array, 0, _array, _arrayIndex, sb._arrayIndex);
161
162 _arrayIndex += sb._arrayIndex;
163
164 return this;
165 }
166
167 public int capacity() {
168 return _array.length;
169 }
170
171 public int index() {
172 return _arrayIndex;
173 }
174
175 public int length() {
176 int length = 0;
177
178 for (int i = 0; i < _arrayIndex; i++) {
179 length += _array[i].length();
180 }
181
182 return length;
183 }
184
185 public void setIndex(int newIndex) {
186 if (newIndex < 0) {
187 throw new ArrayIndexOutOfBoundsException(newIndex);
188 }
189
190 if (newIndex > _array.length) {
191 String[] newArray = new String[newIndex];
192
193 System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
194
195 _array = newArray;
196 }
197
198 if (_arrayIndex < newIndex) {
199 for (int i = _arrayIndex; i < newIndex; i++) {
200 _array[i] = StringPool.BLANK;
201 }
202 }
203
204 if (_arrayIndex > newIndex) {
205 for (int i = newIndex; i < _arrayIndex; i++) {
206 _array[i] = null;
207 }
208 }
209
210 _arrayIndex = newIndex;
211 }
212
213 public void setStringAt(String s, int index) {
214 if ((index < 0) || (index >= _arrayIndex)) {
215 throw new ArrayIndexOutOfBoundsException(index);
216 }
217
218 _array[index] = s;
219 }
220
221 public String stringAt(int index) {
222 if ((index < 0) || (index >= _arrayIndex)) {
223 throw new ArrayIndexOutOfBoundsException(index);
224 }
225
226 return _array[index];
227 }
228
229 @Override
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 final long serialVersionUID = 1L;
337
338 private static ThreadLocal<StringBuilder> _stringBuilderThreadLocal;
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 _stringBuilderThreadLocal =
346 new SoftReferenceThreadLocal<StringBuilder>();
347 _threadLocalBufferLimit = _THREADLOCAL_BUFFER_LIMIT;
348 }
349 else {
350 _stringBuilderThreadLocal = 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 }