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.Writer;
021
022 import java.lang.reflect.Constructor;
023
024
032 public class StringBundler {
033
034 public StringBundler() {
035 _array = new String[_DEFAULT_ARRAY_CAPACITY];
036 }
037
038 public StringBundler(int initialCapacity) {
039 if (initialCapacity <= 0) {
040 initialCapacity = _DEFAULT_ARRAY_CAPACITY;
041 }
042
043 _array = new String[initialCapacity];
044 }
045
046 public StringBundler(String s) {
047 _array = new String[_DEFAULT_ARRAY_CAPACITY];
048
049 _array[0] = s;
050
051 _arrayIndex = 1;
052 }
053
054 public StringBundler(String[] stringArray) {
055 this(stringArray, 0);
056 }
057
058 public StringBundler(String[] stringArray, int extraSpace) {
059 _array = new String[stringArray.length + extraSpace];
060
061 for (int i = 0; i < stringArray.length; i++) {
062 String s = stringArray[i];
063
064 if ((s != null) && (s.length() > 0)) {
065 _array[_arrayIndex++] = s;
066 }
067 }
068 }
069
070 public StringBundler append(boolean b) {
071 if (b) {
072 return append(_TRUE);
073 }
074 else {
075 return append(_FALSE);
076 }
077 }
078
079 public StringBundler append(char c) {
080 return append(String.valueOf(c));
081 }
082
083 public StringBundler append(char[] chars) {
084 if (chars == null) {
085 return append("null");
086 }
087 else {
088 return append(new String(chars));
089 }
090 }
091
092 public StringBundler append(double d) {
093 return append(Double.toString(d));
094 }
095
096 public StringBundler append(float f) {
097 return append(Float.toString(f));
098 }
099
100 public StringBundler append(int i) {
101 return append(Integer.toString(i));
102 }
103
104 public StringBundler append(long l) {
105 return append(Long.toString(l));
106 }
107
108 public StringBundler append(Object obj) {
109 return append(String.valueOf(obj));
110 }
111
112 public StringBundler append(String s) {
113 if (s == null) {
114 s = StringPool.NULL;
115 }
116
117 if (s.length() == 0) {
118 return this;
119 }
120
121 if (_arrayIndex >= _array.length) {
122 expandCapacity(_array.length * 2);
123 }
124
125 _array[_arrayIndex++] = s;
126
127 return this;
128 }
129
130 public StringBundler append(String[] stringArray) {
131 if ((stringArray == null) || (stringArray.length == 0)) {
132 return this;
133 }
134
135 if ((_array.length - _arrayIndex) < stringArray.length) {
136 expandCapacity((_array.length + stringArray.length) * 2);
137 }
138
139 for (int i = 0; i < stringArray.length; i++) {
140 String s = stringArray[i];
141
142 if ((s != null) && (s.length() > 0)) {
143 _array[_arrayIndex++] = s;
144 }
145 }
146
147 return this;
148 }
149
150 public StringBundler append(StringBundler sb) {
151 if ((sb == null) || (sb._arrayIndex == 0)) {
152 return this;
153 }
154
155 if ((_array.length - _arrayIndex) < sb._arrayIndex) {
156 expandCapacity((_array.length + sb._arrayIndex) * 2);
157 }
158
159 System.arraycopy(sb._array, 0, _array, _arrayIndex, sb._arrayIndex);
160
161 _arrayIndex += sb._arrayIndex;
162
163 return this;
164 }
165
166 public int capacity() {
167 return _array.length;
168 }
169
170 public int index() {
171 return _arrayIndex;
172 }
173
174 public int length() {
175 int length = 0;
176
177 for (int i = 0; i < _arrayIndex; i++) {
178 length += _array[i].length();
179 }
180
181 return length;
182 }
183
184 public void setIndex(int newIndex) {
185 if (newIndex < 0) {
186 throw new ArrayIndexOutOfBoundsException(newIndex);
187 }
188
189 if (newIndex > _array.length) {
190 String[] newArray = new String[newIndex];
191
192 System.arraycopy(_array, 0, newArray, 0, _arrayIndex);
193
194 _array = newArray;
195 }
196
197 if (_arrayIndex < newIndex) {
198 for (int i = _arrayIndex; i < newIndex; i++) {
199 _array[i] = StringPool.BLANK;
200 }
201 }
202
203 if (_arrayIndex > newIndex) {
204 for (int i = newIndex; i < _arrayIndex; i++) {
205 _array[i] = null;
206 }
207 }
208
209 _arrayIndex = newIndex;
210 }
211
212 public void setStringAt(String s, int index) {
213 if ((index < 0) || (index >= _arrayIndex)) {
214 throw new ArrayIndexOutOfBoundsException(index);
215 }
216
217 _array[index] = s;
218 }
219
220 public String stringAt(int index) {
221 if ((index < 0) || (index >= _arrayIndex)) {
222 throw new ArrayIndexOutOfBoundsException(index);
223 }
224
225 return _array[index];
226 }
227
228 @Override
229 public String toString() {
230 return toString(true);
231 }
232
233 public String toString(boolean unsafeCreate) {
234 if (_arrayIndex == 0) {
235 return StringPool.BLANK;
236 }
237
238 if (_arrayIndex == 1) {
239 return _array[0];
240 }
241
242 if (_arrayIndex == 2) {
243 return _array[0].concat(_array[1]);
244 }
245
246 if (_arrayIndex == 3) {
247 return _array[0].concat(_array[1]).concat(_array[2]);
248 }
249
250 int length = 0;
251
252 for (int i = 0; i < _arrayIndex; i++) {
253 length += _array[i].length();
254 }
255
256 StringBuilder sb = null;
257
258 if ((length > _unsafeCreateLimit) && (_stringConstructor != null) &&
259 CharBufferPool.isEnabled() && unsafeCreate) {
260
261 char[] charBuffer = CharBufferPool.borrow(length);
262
263 int offset = 0;
264
265 for (int i = 0; i < _arrayIndex; i++) {
266 String s = _array[i];
267
268 s.getChars(0, s.length(), charBuffer, offset);
269
270 offset += s.length();
271 }
272
273 try {
274 return _stringConstructor.newInstance(0, length, charBuffer);
275 }
276 catch (Exception e) {
277 _stringConstructor = null;
278
279 return toString(false);
280 }
281 }
282 else if (length > _threadLocalBufferLimit) {
283 sb = _stringBuilderThreadLocal.get();
284
285 if (sb == null) {
286 sb = new StringBuilder(length);
287
288 _stringBuilderThreadLocal.set(sb);
289 }
290 else if (sb.capacity() < length) {
291 sb.setLength(length);
292 }
293
294 sb.setLength(0);
295 }
296 else {
297 sb = new StringBuilder(length);
298 }
299
300 for (int i = 0; i < _arrayIndex; i++) {
301 sb.append(_array[i]);
302 }
303
304 return sb.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 ThreadLocal<StringBuilder> _stringBuilderThreadLocal;
336 private static Constructor<String> _stringConstructor;
337 private static int _threadLocalBufferLimit;
338 private static int _unsafeCreateLimit;
339
340 static {
341 if (_THREADLOCAL_BUFFER_LIMIT > 0) {
342 _stringBuilderThreadLocal =
343 new SoftReferenceThreadLocal<StringBuilder>();
344 _threadLocalBufferLimit = _THREADLOCAL_BUFFER_LIMIT;
345 }
346 else {
347 _stringBuilderThreadLocal = null;
348 _threadLocalBufferLimit = Integer.MAX_VALUE;
349 }
350
351 if (_UNSAFE_CREATE_LIMIT > 0) {
352 try {
353 _unsafeCreateLimit = _UNSAFE_CREATE_LIMIT;
354
355 _stringConstructor = String.class.getDeclaredConstructor(
356 int.class, int.class, char[].class);
357
358 _stringConstructor.setAccessible(true);
359 }
360 catch (Exception e) {
361 }
362 }
363 else {
364 _unsafeCreateLimit = Integer.MAX_VALUE;
365 _stringConstructor = null;
366 }
367 }
368
369 private String[] _array;
370 private int _arrayIndex;
371
372 }