1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.lang3.builder;
18
19 import java.io.Serializable;
20 import java.lang.reflect.Array;
21 import java.util.Collection;
22 import java.util.Map;
23 import java.util.WeakHashMap;
24
25 import org.apache.commons.lang3.ClassUtils;
26 import org.apache.commons.lang3.ObjectUtils;
27 import org.apache.commons.lang3.StringEscapeUtils;
28 import org.apache.commons.lang3.StringUtils;
29
30 /**
31 * <p>Controls {@code String} formatting for {@link ToStringBuilder}.
32 * The main public interface is always via {@code ToStringBuilder}.</p>
33 *
34 * <p>These classes are intended to be used as {@code Singletons}.
35 * There is no need to instantiate a new style each time. A program
36 * will generally use one of the predefined constants on this class.
37 * Alternatively, the {@link StandardToStringStyle} class can be used
38 * to set the individual settings. Thus most styles can be achieved
39 * without subclassing.</p>
40 *
41 * <p>If required, a subclass can override as many or as few of the
42 * methods as it requires. Each object type (from {@code boolean}
43 * to {@code long} to {@code Object} to {@code int[]}) has
44 * its own methods to output it. Most have two versions, detail and summary.
45 *
46 * <p>For example, the detail version of the array based methods will
47 * output the whole array, whereas the summary method will just output
48 * the array length.</p>
49 *
50 * <p>If you want to format the output of certain objects, such as dates, you
51 * must create a subclass and override a method.
52 * </p>
53 * <pre>
54 * public class MyStyle extends ToStringStyle {
55 * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
56 * if (value instanceof Date) {
57 * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
58 * }
59 * buffer.append(value);
60 * }
61 * }
62 * </pre>
63 *
64 * @since 1.0
65 */
66 @SuppressWarnings("deprecation") // StringEscapeUtils
67 public abstract class ToStringStyle implements Serializable {
68
69 /**
70 * Serialization version ID.
71 */
72 private static final long serialVersionUID = -2587890625525655916L;
73
74 /**
75 * The default toString style. Using the {@code Person}
76 * example from {@link ToStringBuilder}, the output would look like this:
77 *
78 * <pre>
79 * Person@182f0db[name=John Doe,age=33,smoker=false]
80 * </pre>
81 */
82 public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
83
84 /**
85 * The multi line toString style. Using the {@code Person}
86 * example from {@link ToStringBuilder}, the output would look like this:
87 *
88 * <pre>
89 * Person@182f0db[
90 * name=John Doe
91 * age=33
92 * smoker=false
93 * ]
94 * </pre>
95 */
96 public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
97
98 /**
99 * The no field names toString style. Using the
100 * {@code Person} example from {@link ToStringBuilder}, the output
101 * would look like this:
102 *
103 * <pre>
104 * Person@182f0db[John Doe,33,false]
105 * </pre>
106 */
107 public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
108
109 /**
110 * The short prefix toString style. Using the {@code Person} example
111 * from {@link ToStringBuilder}, the output would look like this:
112 *
113 * <pre>
114 * Person[name=John Doe,age=33,smoker=false]
115 * </pre>
116 *
117 * @since 2.1
118 */
119 public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
120
121 /**
122 * The simple toString style. Using the {@code Person}
123 * example from {@link ToStringBuilder}, the output would look like this:
124 *
125 * <pre>
126 * John Doe,33,false
127 * </pre>
128 */
129 public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
130
131 /**
132 * The no class name toString style. Using the {@code Person}
133 * example from {@link ToStringBuilder}, the output would look like this:
134 *
135 * <pre>
136 * [name=John Doe,age=33,smoker=false]
137 * </pre>
138 *
139 * @since 3.4
140 */
141 public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
142
143 /**
144 * The JSON toString style. Using the {@code Person} example from
145 * {@link ToStringBuilder}, the output would look like this:
146 *
147 * <pre>
148 * {"name": "John Doe", "age": 33, "smoker": true}
149 * </pre>
150 *
151 * <strong>Note:</strong> Since field names are mandatory in JSON, this
152 * ToStringStyle will throw an {@link UnsupportedOperationException} if no
153 * field name is passed in while appending. Furthermore This ToStringStyle
154 * will only generate valid JSON if referenced objects also produce JSON
155 * when calling {@code toString()} on them.
156 *
157 * @since 3.4
158 * @see <a href="http://json.org">json.org</a>
159 */
160 public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
161
162 /**
163 * <p>
164 * A registry of objects used by {@code reflectionToString} methods
165 * to detect cyclical object references and avoid infinite loops.
166 * </p>
167 */
168 private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY =
169 new ThreadLocal<>();
170 /*
171 * Note that objects of this class are generally shared between threads, so
172 * an instance variable would not be suitable here.
173 *
174 * In normal use the registry should always be left empty, because the caller
175 * should call toString() which will clean up.
176 *
177 * See LANG-792
178 */
179
180 /**
181 * <p>
182 * Returns the registry of objects being traversed by the {@code reflectionToString}
183 * methods in the current thread.
184 * </p>
185 *
186 * @return Set the registry of objects being traversed
187 */
188 static Map<Object, Object> getRegistry() {
189 return REGISTRY.get();
190 }
191
192 /**
193 * <p>
194 * Returns {@code true} if the registry contains the given object.
195 * Used by the reflection methods to avoid infinite loops.
196 * </p>
197 *
198 * @param value
199 * The object to lookup in the registry.
200 * @return boolean {@code true} if the registry contains the given
201 * object.
202 */
203 static boolean isRegistered(final Object value) {
204 final Map<Object, Object> m = getRegistry();
205 return m != null && m.containsKey(value);
206 }
207
208 /**
209 * <p>
210 * Registers the given object. Used by the reflection methods to avoid
211 * infinite loops.
212 * </p>
213 *
214 * @param value
215 * The object to register.
216 */
217 static void register(final Object value) {
218 if (value != null) {
219 final Map<Object, Object> m = getRegistry();
220 if (m == null) {
221 REGISTRY.set(new WeakHashMap<>());
222 }
223 getRegistry().put(value, null);
224 }
225 }
226
227 /**
228 * <p>
229 * Unregisters the given object.
230 * </p>
231 *
232 * <p>
233 * Used by the reflection methods to avoid infinite loops.
234 * </p>
235 *
236 * @param value
237 * The object to unregister.
238 */
239 static void unregister(final Object value) {
240 if (value != null) {
241 final Map<Object, Object> m = getRegistry();
242 if (m != null) {
243 m.remove(value);
244 if (m.isEmpty()) {
245 REGISTRY.remove();
246 }
247 }
248 }
249 }
250
251 /**
252 * Whether to use the field names, the default is {@code true}.
253 */
254 private boolean useFieldNames = true;
255
256 /**
257 * Whether to use the class name, the default is {@code true}.
258 */
259 private boolean useClassName = true;
260
261 /**
262 * Whether to use short class names, the default is {@code false}.
263 */
264 private boolean useShortClassName = false;
265
266 /**
267 * Whether to use the identity hash code, the default is {@code true}.
268 */
269 private boolean useIdentityHashCode = true;
270
271 /**
272 * The content start {@code '['}.
273 */
274 private String contentStart = "[";
275
276 /**
277 * The content end {@code ']'}.
278 */
279 private String contentEnd = "]";
280
281 /**
282 * The field name value separator {@code '='}.
283 */
284 private String fieldNameValueSeparator = "=";
285
286 /**
287 * Whether the field separator should be added before any other fields.
288 */
289 private boolean fieldSeparatorAtStart = false;
290
291 /**
292 * Whether the field separator should be added after any other fields.
293 */
294 private boolean fieldSeparatorAtEnd = false;
295
296 /**
297 * The field separator {@code ','}.
298 */
299 private String fieldSeparator = ",";
300
301 /**
302 * The array start <code>'{'</code>.
303 */
304 private String arrayStart = "{";
305
306 /**
307 * The array separator {@code ','}.
308 */
309 private String arraySeparator = ",";
310
311 /**
312 * The detail for array content.
313 */
314 private boolean arrayContentDetail = true;
315
316 /**
317 * The array end {@code '}'}.
318 */
319 private String arrayEnd = "}";
320
321 /**
322 * The value to use when fullDetail is {@code null},
323 * the default value is {@code true}.
324 */
325 private boolean defaultFullDetail = true;
326
327 /**
328 * The {@code null} text {@code '<null>'}.
329 */
330 private String nullText = "<null>";
331
332 /**
333 * The summary size text start {@code '<size'}.
334 */
335 private String sizeStartText = "<size=";
336
337 /**
338 * The summary size text start {@code '>'}.
339 */
340 private String sizeEndText = ">";
341
342 /**
343 * The summary object text start {@code '<'}.
344 */
345 private String summaryObjectStartText = "<";
346
347 /**
348 * The summary object text start {@code '>'}.
349 */
350 private String summaryObjectEndText = ">";
351
352 //----------------------------------------------------------------------------
353
354 /**
355 * <p>Constructor.</p>
356 */
357 protected ToStringStyle() {
358 super();
359 }
360
361 //----------------------------------------------------------------------------
362
363 /**
364 * <p>Append to the {@code toString} the superclass toString.</p>
365 * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
366 *
367 * <p>A {@code null} {@code superToString} is ignored.</p>
368 *
369 * @param buffer the {@code StringBuffer} to populate
370 * @param superToString the {@code super.toString()}
371 * @since 2.0
372 */
373 public void appendSuper(final StringBuffer buffer, final String superToString) {
374 appendToString(buffer, superToString);
375 }
376
377 /**
378 * <p>Append to the {@code toString} another toString.</p>
379 * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
380 *
381 * <p>A {@code null} {@code toString} is ignored.</p>
382 *
383 * @param buffer the {@code StringBuffer} to populate
384 * @param toString the additional {@code toString}
385 * @since 2.0
386 */
387 public void appendToString(final StringBuffer buffer, final String toString) {
388 if (toString != null) {
389 final int pos1 = toString.indexOf(contentStart) + contentStart.length();
390 final int pos2 = toString.lastIndexOf(contentEnd);
391 if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
392 if (fieldSeparatorAtStart) {
393 removeLastFieldSeparator(buffer);
394 }
395 buffer.append(toString, pos1, pos2);
396 appendFieldSeparator(buffer);
397 }
398 }
399 }
400
401 /**
402 * <p>Append to the {@code toString} the start of data indicator.</p>
403 *
404 * @param buffer the {@code StringBuffer} to populate
405 * @param object the {@code Object} to build a {@code toString} for
406 */
407 public void appendStart(final StringBuffer buffer, final Object object) {
408 if (object != null) {
409 appendClassName(buffer, object);
410 appendIdentityHashCode(buffer, object);
411 appendContentStart(buffer);
412 if (fieldSeparatorAtStart) {
413 appendFieldSeparator(buffer);
414 }
415 }
416 }
417
418 /**
419 * <p>Append to the {@code toString} the end of data indicator.</p>
420 *
421 * @param buffer the {@code StringBuffer} to populate
422 * @param object the {@code Object} to build a
423 * {@code toString} for.
424 */
425 public void appendEnd(final StringBuffer buffer, final Object object) {
426 if (!this.fieldSeparatorAtEnd) {
427 removeLastFieldSeparator(buffer);
428 }
429 appendContentEnd(buffer);
430 unregister(object);
431 }
432
433 /**
434 * <p>Remove the last field separator from the buffer.</p>
435 *
436 * @param buffer the {@code StringBuffer} to populate
437 * @since 2.0
438 */
439 protected void removeLastFieldSeparator(final StringBuffer buffer) {
440 final int len = buffer.length();
441 final int sepLen = fieldSeparator.length();
442 if (len > 0 && sepLen > 0 && len >= sepLen) {
443 boolean match = true;
444 for (int i = 0; i < sepLen; i++) {
445 if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
446 match = false;
447 break;
448 }
449 }
450 if (match) {
451 buffer.setLength(len - sepLen);
452 }
453 }
454 }
455
456 //----------------------------------------------------------------------------
457
458 /**
459 * <p>Append to the {@code toString} an {@code Object}
460 * value, printing the full {@code toString} of the
461 * {@code Object} passed in.</p>
462 *
463 * @param buffer the {@code StringBuffer} to populate
464 * @param fieldName the field name
465 * @param value the value to add to the {@code toString}
466 * @param fullDetail {@code true} for detail, {@code false}
467 * for summary info, {@code null} for style decides
468 */
469 public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
470 appendFieldStart(buffer, fieldName);
471
472 if (value == null) {
473 appendNullText(buffer, fieldName);
474
475 } else {
476 appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
477 }
478
479 appendFieldEnd(buffer, fieldName);
480 }
481
482 /**
483 * <p>Append to the {@code toString} an {@code Object},
484 * correctly interpreting its type.</p>
485 *
486 * <p>This method performs the main lookup by Class type to correctly
487 * route arrays, {@code Collections}, {@code Maps} and
488 * {@code Objects} to the appropriate method.</p>
489 *
490 * <p>Either detail or summary views can be specified.</p>
491 *
492 * <p>If a cycle is detected, an object will be appended with the
493 * {@code Object.toString()} format.</p>
494 *
495 * @param buffer the {@code StringBuffer} to populate
496 * @param fieldName the field name, typically not used as already appended
497 * @param value the value to add to the {@code toString},
498 * not {@code null}
499 * @param detail output detail or not
500 */
501 protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
502 if (isRegistered(value)
503 && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
504 appendCyclicObject(buffer, fieldName, value);
505 return;
506 }
507
508 register(value);
509
510 try {
511 if (value instanceof Collection<?>) {
512 if (detail) {
513 appendDetail(buffer, fieldName, (Collection<?>) value);
514 } else {
515 appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
516 }
517
518 } else if (value instanceof Map<?, ?>) {
519 if (detail) {
520 appendDetail(buffer, fieldName, (Map<?, ?>) value);
521 } else {
522 appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
523 }
524
525 } else if (value instanceof long[]) {
526 if (detail) {
527 appendDetail(buffer, fieldName, (long[]) value);
528 } else {
529 appendSummary(buffer, fieldName, (long[]) value);
530 }
531
532 } else if (value instanceof int[]) {
533 if (detail) {
534 appendDetail(buffer, fieldName, (int[]) value);
535 } else {
536 appendSummary(buffer, fieldName, (int[]) value);
537 }
538
539 } else if (value instanceof short[]) {
540 if (detail) {
541 appendDetail(buffer, fieldName, (short[]) value);
542 } else {
543 appendSummary(buffer, fieldName, (short[]) value);
544 }
545
546 } else if (value instanceof byte[]) {
547 if (detail) {
548 appendDetail(buffer, fieldName, (byte[]) value);
549 } else {
550 appendSummary(buffer, fieldName, (byte[]) value);
551 }
552
553 } else if (value instanceof char[]) {
554 if (detail) {
555 appendDetail(buffer, fieldName, (char[]) value);
556 } else {
557 appendSummary(buffer, fieldName, (char[]) value);
558 }
559
560 } else if (value instanceof double[]) {
561 if (detail) {
562 appendDetail(buffer, fieldName, (double[]) value);
563 } else {
564 appendSummary(buffer, fieldName, (double[]) value);
565 }
566
567 } else if (value instanceof float[]) {
568 if (detail) {
569 appendDetail(buffer, fieldName, (float[]) value);
570 } else {
571 appendSummary(buffer, fieldName, (float[]) value);
572 }
573
574 } else if (value instanceof boolean[]) {
575 if (detail) {
576 appendDetail(buffer, fieldName, (boolean[]) value);
577 } else {
578 appendSummary(buffer, fieldName, (boolean[]) value);
579 }
580
581 } else if (value.getClass().isArray()) {
582 if (detail) {
583 appendDetail(buffer, fieldName, (Object[]) value);
584 } else {
585 appendSummary(buffer, fieldName, (Object[]) value);
586 }
587
588 } else {
589 if (detail) {
590 appendDetail(buffer, fieldName, value);
591 } else {
592 appendSummary(buffer, fieldName, value);
593 }
594 }
595 } finally {
596 unregister(value);
597 }
598 }
599
600 /**
601 * <p>Append to the {@code toString} an {@code Object}
602 * value that has been detected to participate in a cycle. This
603 * implementation will print the standard string value of the value.</p>
604 *
605 * @param buffer the {@code StringBuffer} to populate
606 * @param fieldName the field name, typically not used as already appended
607 * @param value the value to add to the {@code toString},
608 * not {@code null}
609 *
610 * @since 2.2
611 */
612 protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
613 ObjectUtils.identityToString(buffer, value);
614 }
615
616 /**
617 * <p>Append to the {@code toString} an {@code Object}
618 * value, printing the full detail of the {@code Object}.</p>
619 *
620 * @param buffer the {@code StringBuffer} to populate
621 * @param fieldName the field name, typically not used as already appended
622 * @param value the value to add to the {@code toString},
623 * not {@code null}
624 */
625 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
626 buffer.append(value);
627 }
628
629 /**
630 * <p>Append to the {@code toString} a {@code Collection}.</p>
631 *
632 * @param buffer the {@code StringBuffer} to populate
633 * @param fieldName the field name, typically not used as already appended
634 * @param coll the {@code Collection} to add to the
635 * {@code toString}, not {@code null}
636 */
637 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
638 buffer.append(coll);
639 }
640
641 /**
642 * <p>Append to the {@code toString} a {@code Map}.</p>
643 *
644 * @param buffer the {@code StringBuffer} to populate
645 * @param fieldName the field name, typically not used as already appended
646 * @param map the {@code Map} to add to the {@code toString},
647 * not {@code null}
648 */
649 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
650 buffer.append(map);
651 }
652
653 /**
654 * <p>Append to the {@code toString} an {@code Object}
655 * value, printing a summary of the {@code Object}.</P>
656 *
657 * @param buffer the {@code StringBuffer} to populate
658 * @param fieldName the field name, typically not used as already appended
659 * @param value the value to add to the {@code toString},
660 * not {@code null}
661 */
662 protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
663 buffer.append(summaryObjectStartText);
664 buffer.append(getShortClassName(value.getClass()));
665 buffer.append(summaryObjectEndText);
666 }
667
668 //----------------------------------------------------------------------------
669
670 /**
671 * <p>Append to the {@code toString} a {@code long}
672 * value.</p>
673 *
674 * @param buffer the {@code StringBuffer} to populate
675 * @param fieldName the field name
676 * @param value the value to add to the {@code toString}
677 */
678 public void append(final StringBuffer buffer, final String fieldName, final long value) {
679 appendFieldStart(buffer, fieldName);
680 appendDetail(buffer, fieldName, value);
681 appendFieldEnd(buffer, fieldName);
682 }
683
684 /**
685 * <p>Append to the {@code toString} a {@code long}
686 * value.</p>
687 *
688 * @param buffer the {@code StringBuffer} to populate
689 * @param fieldName the field name, typically not used as already appended
690 * @param value the value to add to the {@code toString}
691 */
692 protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
693 buffer.append(value);
694 }
695
696 //----------------------------------------------------------------------------
697
698 /**
699 * <p>Append to the {@code toString} an {@code int}
700 * value.</p>
701 *
702 * @param buffer the {@code StringBuffer} to populate
703 * @param fieldName the field name
704 * @param value the value to add to the {@code toString}
705 */
706 public void append(final StringBuffer buffer, final String fieldName, final int value) {
707 appendFieldStart(buffer, fieldName);
708 appendDetail(buffer, fieldName, value);
709 appendFieldEnd(buffer, fieldName);
710 }
711
712 /**
713 * <p>Append to the {@code toString} an {@code int}
714 * value.</p>
715 *
716 * @param buffer the {@code StringBuffer} to populate
717 * @param fieldName the field name, typically not used as already appended
718 * @param value the value to add to the {@code toString}
719 */
720 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
721 buffer.append(value);
722 }
723
724 //----------------------------------------------------------------------------
725
726 /**
727 * <p>Append to the {@code toString} a {@code short}
728 * value.</p>
729 *
730 * @param buffer the {@code StringBuffer} to populate
731 * @param fieldName the field name
732 * @param value the value to add to the {@code toString}
733 */
734 public void append(final StringBuffer buffer, final String fieldName, final short value) {
735 appendFieldStart(buffer, fieldName);
736 appendDetail(buffer, fieldName, value);
737 appendFieldEnd(buffer, fieldName);
738 }
739
740 /**
741 * <p>Append to the {@code toString} a {@code short}
742 * value.</p>
743 *
744 * @param buffer the {@code StringBuffer} to populate
745 * @param fieldName the field name, typically not used as already appended
746 * @param value the value to add to the {@code toString}
747 */
748 protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
749 buffer.append(value);
750 }
751
752 //----------------------------------------------------------------------------
753
754 /**
755 * <p>Append to the {@code toString} a {@code byte}
756 * value.</p>
757 *
758 * @param buffer the {@code StringBuffer} to populate
759 * @param fieldName the field name
760 * @param value the value to add to the {@code toString}
761 */
762 public void append(final StringBuffer buffer, final String fieldName, final byte value) {
763 appendFieldStart(buffer, fieldName);
764 appendDetail(buffer, fieldName, value);
765 appendFieldEnd(buffer, fieldName);
766 }
767
768 /**
769 * <p>Append to the {@code toString} a {@code byte}
770 * value.</p>
771 *
772 * @param buffer the {@code StringBuffer} to populate
773 * @param fieldName the field name, typically not used as already appended
774 * @param value the value to add to the {@code toString}
775 */
776 protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
777 buffer.append(value);
778 }
779
780 //----------------------------------------------------------------------------
781
782 /**
783 * <p>Append to the {@code toString} a {@code char}
784 * value.</p>
785 *
786 * @param buffer the {@code StringBuffer} to populate
787 * @param fieldName the field name
788 * @param value the value to add to the {@code toString}
789 */
790 public void append(final StringBuffer buffer, final String fieldName, final char value) {
791 appendFieldStart(buffer, fieldName);
792 appendDetail(buffer, fieldName, value);
793 appendFieldEnd(buffer, fieldName);
794 }
795
796 /**
797 * <p>Append to the {@code toString} a {@code char}
798 * value.</p>
799 *
800 * @param buffer the {@code StringBuffer} to populate
801 * @param fieldName the field name, typically not used as already appended
802 * @param value the value to add to the {@code toString}
803 */
804 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
805 buffer.append(value);
806 }
807
808 //----------------------------------------------------------------------------
809
810 /**
811 * <p>Append to the {@code toString} a {@code double}
812 * value.</p>
813 *
814 * @param buffer the {@code StringBuffer} to populate
815 * @param fieldName the field name
816 * @param value the value to add to the {@code toString}
817 */
818 public void append(final StringBuffer buffer, final String fieldName, final double value) {
819 appendFieldStart(buffer, fieldName);
820 appendDetail(buffer, fieldName, value);
821 appendFieldEnd(buffer, fieldName);
822 }
823
824 /**
825 * <p>Append to the {@code toString} a {@code double}
826 * value.</p>
827 *
828 * @param buffer the {@code StringBuffer} to populate
829 * @param fieldName the field name, typically not used as already appended
830 * @param value the value to add to the {@code toString}
831 */
832 protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
833 buffer.append(value);
834 }
835
836 //----------------------------------------------------------------------------
837
838 /**
839 * <p>Append to the {@code toString} a {@code float}
840 * value.</p>
841 *
842 * @param buffer the {@code StringBuffer} to populate
843 * @param fieldName the field name
844 * @param value the value to add to the {@code toString}
845 */
846 public void append(final StringBuffer buffer, final String fieldName, final float value) {
847 appendFieldStart(buffer, fieldName);
848 appendDetail(buffer, fieldName, value);
849 appendFieldEnd(buffer, fieldName);
850 }
851
852 /**
853 * <p>Append to the {@code toString} a {@code float}
854 * value.</p>
855 *
856 * @param buffer the {@code StringBuffer} to populate
857 * @param fieldName the field name, typically not used as already appended
858 * @param value the value to add to the {@code toString}
859 */
860 protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
861 buffer.append(value);
862 }
863
864 //----------------------------------------------------------------------------
865
866 /**
867 * <p>Append to the {@code toString} a {@code boolean}
868 * value.</p>
869 *
870 * @param buffer the {@code StringBuffer} to populate
871 * @param fieldName the field name
872 * @param value the value to add to the {@code toString}
873 */
874 public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
875 appendFieldStart(buffer, fieldName);
876 appendDetail(buffer, fieldName, value);
877 appendFieldEnd(buffer, fieldName);
878 }
879
880 /**
881 * <p>Append to the {@code toString} a {@code boolean}
882 * value.</p>
883 *
884 * @param buffer the {@code StringBuffer} to populate
885 * @param fieldName the field name, typically not used as already appended
886 * @param value the value to add to the {@code toString}
887 */
888 protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
889 buffer.append(value);
890 }
891
892 /**
893 * <p>Append to the {@code toString} an {@code Object}
894 * array.</p>
895 *
896 * @param buffer the {@code StringBuffer} to populate
897 * @param fieldName the field name
898 * @param array the array to add to the toString
899 * @param fullDetail {@code true} for detail, {@code false}
900 * for summary info, {@code null} for style decides
901 */
902 public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
903 appendFieldStart(buffer, fieldName);
904
905 if (array == null) {
906 appendNullText(buffer, fieldName);
907
908 } else if (isFullDetail(fullDetail)) {
909 appendDetail(buffer, fieldName, array);
910
911 } else {
912 appendSummary(buffer, fieldName, array);
913 }
914
915 appendFieldEnd(buffer, fieldName);
916 }
917
918 //----------------------------------------------------------------------------
919
920 /**
921 * <p>Append to the {@code toString} the detail of an
922 * {@code Object} array.</p>
923 *
924 * @param buffer the {@code StringBuffer} to populate
925 * @param fieldName the field name, typically not used as already appended
926 * @param array the array to add to the {@code toString},
927 * not {@code null}
928 */
929 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
930 buffer.append(arrayStart);
931 for (int i = 0; i < array.length; i++) {
932 final Object item = array[i];
933 if (i > 0) {
934 buffer.append(arraySeparator);
935 }
936 if (item == null) {
937 appendNullText(buffer, fieldName);
938
939 } else {
940 appendInternal(buffer, fieldName, item, arrayContentDetail);
941 }
942 }
943 buffer.append(arrayEnd);
944 }
945
946 /**
947 * <p>Append to the {@code toString} the detail of an array type.</p>
948 *
949 * @param buffer the {@code StringBuffer} to populate
950 * @param fieldName the field name, typically not used as already appended
951 * @param array the array to add to the {@code toString},
952 * not {@code null}
953 * @since 2.0
954 */
955 protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
956 buffer.append(arrayStart);
957 final int length = Array.getLength(array);
958 for (int i = 0; i < length; i++) {
959 final Object item = Array.get(array, i);
960 if (i > 0) {
961 buffer.append(arraySeparator);
962 }
963 if (item == null) {
964 appendNullText(buffer, fieldName);
965
966 } else {
967 appendInternal(buffer, fieldName, item, arrayContentDetail);
968 }
969 }
970 buffer.append(arrayEnd);
971 }
972
973 /**
974 * <p>Append to the {@code toString} a summary of an
975 * {@code Object} array.</p>
976 *
977 * @param buffer the {@code StringBuffer} to populate
978 * @param fieldName the field name, typically not used as already appended
979 * @param array the array to add to the {@code toString},
980 * not {@code null}
981 */
982 protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
983 appendSummarySize(buffer, fieldName, array.length);
984 }
985
986 //----------------------------------------------------------------------------
987
988 /**
989 * <p>Append to the {@code toString} a {@code long}
990 * array.</p>
991 *
992 * @param buffer the {@code StringBuffer} to populate
993 * @param fieldName the field name
994 * @param array the array to add to the {@code toString}
995 * @param fullDetail {@code true} for detail, {@code false}
996 * for summary info, {@code null} for style decides
997 */
998 public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
999 appendFieldStart(buffer, fieldName);
1000
1001 if (array == null) {
1002 appendNullText(buffer, fieldName);
1003
1004 } else if (isFullDetail(fullDetail)) {
1005 appendDetail(buffer, fieldName, array);
1006
1007 } else {
1008 appendSummary(buffer, fieldName, array);
1009 }
1010
1011 appendFieldEnd(buffer, fieldName);
1012 }
1013
1014 /**
1015 * <p>Append to the {@code toString} the detail of a
1016 * {@code long} array.</p>
1017 *
1018 * @param buffer the {@code StringBuffer} to populate
1019 * @param fieldName the field name, typically not used as already appended
1020 * @param array the array to add to the {@code toString},
1021 * not {@code null}
1022 */
1023 protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
1024 buffer.append(arrayStart);
1025 for (int i = 0; i < array.length; i++) {
1026 if (i > 0) {
1027 buffer.append(arraySeparator);
1028 }
1029 appendDetail(buffer, fieldName, array[i]);
1030 }
1031 buffer.append(arrayEnd);
1032 }
1033
1034 /**
1035 * <p>Append to the {@code toString} a summary of a
1036 * {@code long} array.</p>
1037 *
1038 * @param buffer the {@code StringBuffer} to populate
1039 * @param fieldName the field name, typically not used as already appended
1040 * @param array the array to add to the {@code toString},
1041 * not {@code null}
1042 */
1043 protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
1044 appendSummarySize(buffer, fieldName, array.length);
1045 }
1046
1047 //----------------------------------------------------------------------------
1048
1049 /**
1050 * <p>Append to the {@code toString} an {@code int}
1051 * array.</p>
1052 *
1053 * @param buffer the {@code StringBuffer} to populate
1054 * @param fieldName the field name
1055 * @param array the array to add to the {@code toString}
1056 * @param fullDetail {@code true} for detail, {@code false}
1057 * for summary info, {@code null} for style decides
1058 */
1059 public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
1060 appendFieldStart(buffer, fieldName);
1061
1062 if (array == null) {
1063 appendNullText(buffer, fieldName);
1064
1065 } else if (isFullDetail(fullDetail)) {
1066 appendDetail(buffer, fieldName, array);
1067
1068 } else {
1069 appendSummary(buffer, fieldName, array);
1070 }
1071
1072 appendFieldEnd(buffer, fieldName);
1073 }
1074
1075 /**
1076 * <p>Append to the {@code toString} the detail of an
1077 * {@code int} array.</p>
1078 *
1079 * @param buffer the {@code StringBuffer} to populate
1080 * @param fieldName the field name, typically not used as already appended
1081 * @param array the array to add to the {@code toString},
1082 * not {@code null}
1083 */
1084 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
1085 buffer.append(arrayStart);
1086 for (int i = 0; i < array.length; i++) {
1087 if (i > 0) {
1088 buffer.append(arraySeparator);
1089 }
1090 appendDetail(buffer, fieldName, array[i]);
1091 }
1092 buffer.append(arrayEnd);
1093 }
1094
1095 /**
1096 * <p>Append to the {@code toString} a summary of an
1097 * {@code int} array.</p>
1098 *
1099 * @param buffer the {@code StringBuffer} to populate
1100 * @param fieldName the field name, typically not used as already appended
1101 * @param array the array to add to the {@code toString},
1102 * not {@code null}
1103 */
1104 protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
1105 appendSummarySize(buffer, fieldName, array.length);
1106 }
1107
1108 //----------------------------------------------------------------------------
1109
1110 /**
1111 * <p>Append to the {@code toString} a {@code short}
1112 * array.</p>
1113 *
1114 * @param buffer the {@code StringBuffer} to populate
1115 * @param fieldName the field name
1116 * @param array the array to add to the {@code toString}
1117 * @param fullDetail {@code true} for detail, {@code false}
1118 * for summary info, {@code null} for style decides
1119 */
1120 public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
1121 appendFieldStart(buffer, fieldName);
1122
1123 if (array == null) {
1124 appendNullText(buffer, fieldName);
1125
1126 } else if (isFullDetail(fullDetail)) {
1127 appendDetail(buffer, fieldName, array);
1128
1129 } else {
1130 appendSummary(buffer, fieldName, array);
1131 }
1132
1133 appendFieldEnd(buffer, fieldName);
1134 }
1135
1136 /**
1137 * <p>Append to the {@code toString} the detail of a
1138 * {@code short} array.</p>
1139 *
1140 * @param buffer the {@code StringBuffer} to populate
1141 * @param fieldName the field name, typically not used as already appended
1142 * @param array the array to add to the {@code toString},
1143 * not {@code null}
1144 */
1145 protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
1146 buffer.append(arrayStart);
1147 for (int i = 0; i < array.length; i++) {
1148 if (i > 0) {
1149 buffer.append(arraySeparator);
1150 }
1151 appendDetail(buffer, fieldName, array[i]);
1152 }
1153 buffer.append(arrayEnd);
1154 }
1155
1156 /**
1157 * <p>Append to the {@code toString} a summary of a
1158 * {@code short} array.</p>
1159 *
1160 * @param buffer the {@code StringBuffer} to populate
1161 * @param fieldName the field name, typically not used as already appended
1162 * @param array the array to add to the {@code toString},
1163 * not {@code null}
1164 */
1165 protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
1166 appendSummarySize(buffer, fieldName, array.length);
1167 }
1168
1169 //----------------------------------------------------------------------------
1170
1171 /**
1172 * <p>Append to the {@code toString} a {@code byte}
1173 * array.</p>
1174 *
1175 * @param buffer the {@code StringBuffer} to populate
1176 * @param fieldName the field name
1177 * @param array the array to add to the {@code toString}
1178 * @param fullDetail {@code true} for detail, {@code false}
1179 * for summary info, {@code null} for style decides
1180 */
1181 public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
1182 appendFieldStart(buffer, fieldName);
1183
1184 if (array == null) {
1185 appendNullText(buffer, fieldName);
1186
1187 } else if (isFullDetail(fullDetail)) {
1188 appendDetail(buffer, fieldName, array);
1189
1190 } else {
1191 appendSummary(buffer, fieldName, array);
1192 }
1193
1194 appendFieldEnd(buffer, fieldName);
1195 }
1196
1197 /**
1198 * <p>Append to the {@code toString} the detail of a
1199 * {@code byte} array.</p>
1200 *
1201 * @param buffer the {@code StringBuffer} to populate
1202 * @param fieldName the field name, typically not used as already appended
1203 * @param array the array to add to the {@code toString},
1204 * not {@code null}
1205 */
1206 protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
1207 buffer.append(arrayStart);
1208 for (int i = 0; i < array.length; i++) {
1209 if (i > 0) {
1210 buffer.append(arraySeparator);
1211 }
1212 appendDetail(buffer, fieldName, array[i]);
1213 }
1214 buffer.append(arrayEnd);
1215 }
1216
1217 /**
1218 * <p>Append to the {@code toString} a summary of a
1219 * {@code byte} array.</p>
1220 *
1221 * @param buffer the {@code StringBuffer} to populate
1222 * @param fieldName the field name, typically not used as already appended
1223 * @param array the array to add to the {@code toString},
1224 * not {@code null}
1225 */
1226 protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
1227 appendSummarySize(buffer, fieldName, array.length);
1228 }
1229
1230 //----------------------------------------------------------------------------
1231
1232 /**
1233 * <p>Append to the {@code toString} a {@code char}
1234 * array.</p>
1235 *
1236 * @param buffer the {@code StringBuffer} to populate
1237 * @param fieldName the field name
1238 * @param array the array to add to the {@code toString}
1239 * @param fullDetail {@code true} for detail, {@code false}
1240 * for summary info, {@code null} for style decides
1241 */
1242 public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
1243 appendFieldStart(buffer, fieldName);
1244
1245 if (array == null) {
1246 appendNullText(buffer, fieldName);
1247
1248 } else if (isFullDetail(fullDetail)) {
1249 appendDetail(buffer, fieldName, array);
1250
1251 } else {
1252 appendSummary(buffer, fieldName, array);
1253 }
1254
1255 appendFieldEnd(buffer, fieldName);
1256 }
1257
1258 /**
1259 * <p>Append to the {@code toString} the detail of a
1260 * {@code char} array.</p>
1261 *
1262 * @param buffer the {@code StringBuffer} to populate
1263 * @param fieldName the field name, typically not used as already appended
1264 * @param array the array to add to the {@code toString},
1265 * not {@code null}
1266 */
1267 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
1268 buffer.append(arrayStart);
1269 for (int i = 0; i < array.length; i++) {
1270 if (i > 0) {
1271 buffer.append(arraySeparator);
1272 }
1273 appendDetail(buffer, fieldName, array[i]);
1274 }
1275 buffer.append(arrayEnd);
1276 }
1277
1278 /**
1279 * <p>Append to the {@code toString} a summary of a
1280 * {@code char} array.</p>
1281 *
1282 * @param buffer the {@code StringBuffer} to populate
1283 * @param fieldName the field name, typically not used as already appended
1284 * @param array the array to add to the {@code toString},
1285 * not {@code null}
1286 */
1287 protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
1288 appendSummarySize(buffer, fieldName, array.length);
1289 }
1290
1291 //----------------------------------------------------------------------------
1292
1293 /**
1294 * <p>Append to the {@code toString} a {@code double}
1295 * array.</p>
1296 *
1297 * @param buffer the {@code StringBuffer} to populate
1298 * @param fieldName the field name
1299 * @param array the array to add to the toString
1300 * @param fullDetail {@code true} for detail, {@code false}
1301 * for summary info, {@code null} for style decides
1302 */
1303 public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
1304 appendFieldStart(buffer, fieldName);
1305
1306 if (array == null) {
1307 appendNullText(buffer, fieldName);
1308
1309 } else if (isFullDetail(fullDetail)) {
1310 appendDetail(buffer, fieldName, array);
1311
1312 } else {
1313 appendSummary(buffer, fieldName, array);
1314 }
1315
1316 appendFieldEnd(buffer, fieldName);
1317 }
1318
1319 /**
1320 * <p>Append to the {@code toString} the detail of a
1321 * {@code double} array.</p>
1322 *
1323 * @param buffer the {@code StringBuffer} to populate
1324 * @param fieldName the field name, typically not used as already appended
1325 * @param array the array to add to the {@code toString},
1326 * not {@code null}
1327 */
1328 protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
1329 buffer.append(arrayStart);
1330 for (int i = 0; i < array.length; i++) {
1331 if (i > 0) {
1332 buffer.append(arraySeparator);
1333 }
1334 appendDetail(buffer, fieldName, array[i]);
1335 }
1336 buffer.append(arrayEnd);
1337 }
1338
1339 /**
1340 * <p>Append to the {@code toString} a summary of a
1341 * {@code double} array.</p>
1342 *
1343 * @param buffer the {@code StringBuffer} to populate
1344 * @param fieldName the field name, typically not used as already appended
1345 * @param array the array to add to the {@code toString},
1346 * not {@code null}
1347 */
1348 protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
1349 appendSummarySize(buffer, fieldName, array.length);
1350 }
1351
1352 //----------------------------------------------------------------------------
1353
1354 /**
1355 * <p>Append to the {@code toString} a {@code float}
1356 * array.</p>
1357 *
1358 * @param buffer the {@code StringBuffer} to populate
1359 * @param fieldName the field name
1360 * @param array the array to add to the toString
1361 * @param fullDetail {@code true} for detail, {@code false}
1362 * for summary info, {@code null} for style decides
1363 */
1364 public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
1365 appendFieldStart(buffer, fieldName);
1366
1367 if (array == null) {
1368 appendNullText(buffer, fieldName);
1369
1370 } else if (isFullDetail(fullDetail)) {
1371 appendDetail(buffer, fieldName, array);
1372
1373 } else {
1374 appendSummary(buffer, fieldName, array);
1375 }
1376
1377 appendFieldEnd(buffer, fieldName);
1378 }
1379
1380 /**
1381 * <p>Append to the {@code toString} the detail of a
1382 * {@code float} array.</p>
1383 *
1384 * @param buffer the {@code StringBuffer} to populate
1385 * @param fieldName the field name, typically not used as already appended
1386 * @param array the array to add to the {@code toString},
1387 * not {@code null}
1388 */
1389 protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
1390 buffer.append(arrayStart);
1391 for (int i = 0; i < array.length; i++) {
1392 if (i > 0) {
1393 buffer.append(arraySeparator);
1394 }
1395 appendDetail(buffer, fieldName, array[i]);
1396 }
1397 buffer.append(arrayEnd);
1398 }
1399
1400 /**
1401 * <p>Append to the {@code toString} a summary of a
1402 * {@code float} array.</p>
1403 *
1404 * @param buffer the {@code StringBuffer} to populate
1405 * @param fieldName the field name, typically not used as already appended
1406 * @param array the array to add to the {@code toString},
1407 * not {@code null}
1408 */
1409 protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
1410 appendSummarySize(buffer, fieldName, array.length);
1411 }
1412
1413 //----------------------------------------------------------------------------
1414
1415 /**
1416 * <p>Append to the {@code toString} a {@code boolean}
1417 * array.</p>
1418 *
1419 * @param buffer the {@code StringBuffer} to populate
1420 * @param fieldName the field name
1421 * @param array the array to add to the toString
1422 * @param fullDetail {@code true} for detail, {@code false}
1423 * for summary info, {@code null} for style decides
1424 */
1425 public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
1426 appendFieldStart(buffer, fieldName);
1427
1428 if (array == null) {
1429 appendNullText(buffer, fieldName);
1430
1431 } else if (isFullDetail(fullDetail)) {
1432 appendDetail(buffer, fieldName, array);
1433
1434 } else {
1435 appendSummary(buffer, fieldName, array);
1436 }
1437
1438 appendFieldEnd(buffer, fieldName);
1439 }
1440
1441 /**
1442 * <p>Append to the {@code toString} the detail of a
1443 * {@code boolean} array.</p>
1444 *
1445 * @param buffer the {@code StringBuffer} to populate
1446 * @param fieldName the field name, typically not used as already appended
1447 * @param array the array to add to the {@code toString},
1448 * not {@code null}
1449 */
1450 protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1451 buffer.append(arrayStart);
1452 for (int i = 0; i < array.length; i++) {
1453 if (i > 0) {
1454 buffer.append(arraySeparator);
1455 }
1456 appendDetail(buffer, fieldName, array[i]);
1457 }
1458 buffer.append(arrayEnd);
1459 }
1460
1461 /**
1462 * <p>Append to the {@code toString} a summary of a
1463 * {@code boolean} array.</p>
1464 *
1465 * @param buffer the {@code StringBuffer} to populate
1466 * @param fieldName the field name, typically not used as already appended
1467 * @param array the array to add to the {@code toString},
1468 * not {@code null}
1469 */
1470 protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1471 appendSummarySize(buffer, fieldName, array.length);
1472 }
1473
1474 //----------------------------------------------------------------------------
1475
1476 /**
1477 * <p>Append to the {@code toString} the class name.</p>
1478 *
1479 * @param buffer the {@code StringBuffer} to populate
1480 * @param object the {@code Object} whose name to output
1481 */
1482 protected void appendClassName(final StringBuffer buffer, final Object object) {
1483 if (useClassName && object != null) {
1484 register(object);
1485 if (useShortClassName) {
1486 buffer.append(getShortClassName(object.getClass()));
1487 } else {
1488 buffer.append(object.getClass().getName());
1489 }
1490 }
1491 }
1492
1493 /**
1494 * <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p>
1495 *
1496 * @param buffer the {@code StringBuffer} to populate
1497 * @param object the {@code Object} whose id to output
1498 */
1499 protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
1500 if (this.isUseIdentityHashCode() && object!=null) {
1501 register(object);
1502 buffer.append('@');
1503 buffer.append(Integer.toHexString(System.identityHashCode(object)));
1504 }
1505 }
1506
1507 /**
1508 * <p>Append to the {@code toString} the content start.</p>
1509 *
1510 * @param buffer the {@code StringBuffer} to populate
1511 */
1512 protected void appendContentStart(final StringBuffer buffer) {
1513 buffer.append(contentStart);
1514 }
1515
1516 /**
1517 * <p>Append to the {@code toString} the content end.</p>
1518 *
1519 * @param buffer the {@code StringBuffer} to populate
1520 */
1521 protected void appendContentEnd(final StringBuffer buffer) {
1522 buffer.append(contentEnd);
1523 }
1524
1525 /**
1526 * <p>Append to the {@code toString} an indicator for {@code null}.</p>
1527 *
1528 * <p>The default indicator is {@code '<null>'}.</p>
1529 *
1530 * @param buffer the {@code StringBuffer} to populate
1531 * @param fieldName the field name, typically not used as already appended
1532 */
1533 protected void appendNullText(final StringBuffer buffer, final String fieldName) {
1534 buffer.append(nullText);
1535 }
1536
1537 /**
1538 * <p>Append to the {@code toString} the field separator.</p>
1539 *
1540 * @param buffer the {@code StringBuffer} to populate
1541 */
1542 protected void appendFieldSeparator(final StringBuffer buffer) {
1543 buffer.append(fieldSeparator);
1544 }
1545
1546 /**
1547 * <p>Append to the {@code toString} the field start.</p>
1548 *
1549 * @param buffer the {@code StringBuffer} to populate
1550 * @param fieldName the field name
1551 */
1552 protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
1553 if (useFieldNames && fieldName != null) {
1554 buffer.append(fieldName);
1555 buffer.append(fieldNameValueSeparator);
1556 }
1557 }
1558
1559 /**
1560 * <p>Append to the {@code toString} the field end.</p>
1561 *
1562 * @param buffer the {@code StringBuffer} to populate
1563 * @param fieldName the field name, typically not used as already appended
1564 */
1565 protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
1566 appendFieldSeparator(buffer);
1567 }
1568
1569 /**
1570 * <p>Append to the {@code toString} a size summary.</p>
1571 *
1572 * <p>The size summary is used to summarize the contents of
1573 * {@code Collections}, {@code Maps} and arrays.</p>
1574 *
1575 * <p>The output consists of a prefix, the passed in size
1576 * and a suffix.</p>
1577 *
1578 * <p>The default format is {@code '<size=n>'}.</p>
1579 *
1580 * @param buffer the {@code StringBuffer} to populate
1581 * @param fieldName the field name, typically not used as already appended
1582 * @param size the size to append
1583 */
1584 protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
1585 buffer.append(sizeStartText);
1586 buffer.append(size);
1587 buffer.append(sizeEndText);
1588 }
1589
1590 /**
1591 * <p>Is this field to be output in full detail.</p>
1592 *
1593 * <p>This method converts a detail request into a detail level.
1594 * The calling code may request full detail ({@code true}),
1595 * but a subclass might ignore that and always return
1596 * {@code false}. The calling code may pass in
1597 * {@code null} indicating that it doesn't care about
1598 * the detail level. In this case the default detail level is
1599 * used.</p>
1600 *
1601 * @param fullDetailRequest the detail level requested
1602 * @return whether full detail is to be shown
1603 */
1604 protected boolean isFullDetail(final Boolean fullDetailRequest) {
1605 if (fullDetailRequest == null) {
1606 return defaultFullDetail;
1607 }
1608 return fullDetailRequest.booleanValue();
1609 }
1610
1611 /**
1612 * <p>Gets the short class name for a class.</p>
1613 *
1614 * <p>The short class name is the classname excluding
1615 * the package name.</p>
1616 *
1617 * @param cls the {@code Class} to get the short name of
1618 * @return the short name
1619 */
1620 protected String getShortClassName(final Class<?> cls) {
1621 return ClassUtils.getShortClassName(cls);
1622 }
1623
1624 // Setters and getters for the customizable parts of the style
1625 // These methods are not expected to be overridden, except to make public
1626 // (They are not public so that immutable subclasses can be written)
1627 //---------------------------------------------------------------------
1628
1629 /**
1630 * <p>Gets whether to use the class name.</p>
1631 *
1632 * @return the current useClassName flag
1633 */
1634 protected boolean isUseClassName() {
1635 return useClassName;
1636 }
1637
1638 /**
1639 * <p>Sets whether to use the class name.</p>
1640 *
1641 * @param useClassName the new useClassName flag
1642 */
1643 protected void setUseClassName(final boolean useClassName) {
1644 this.useClassName = useClassName;
1645 }
1646
1647 //---------------------------------------------------------------------
1648
1649 /**
1650 * <p>Gets whether to output short or long class names.</p>
1651 *
1652 * @return the current useShortClassName flag
1653 * @since 2.0
1654 */
1655 protected boolean isUseShortClassName() {
1656 return useShortClassName;
1657 }
1658
1659 /**
1660 * <p>Sets whether to output short or long class names.</p>
1661 *
1662 * @param useShortClassName the new useShortClassName flag
1663 * @since 2.0
1664 */
1665 protected void setUseShortClassName(final boolean useShortClassName) {
1666 this.useShortClassName = useShortClassName;
1667 }
1668
1669 //---------------------------------------------------------------------
1670
1671 /**
1672 * <p>Gets whether to use the identity hash code.</p>
1673 *
1674 * @return the current useIdentityHashCode flag
1675 */
1676 protected boolean isUseIdentityHashCode() {
1677 return useIdentityHashCode;
1678 }
1679
1680 /**
1681 * <p>Sets whether to use the identity hash code.</p>
1682 *
1683 * @param useIdentityHashCode the new useIdentityHashCode flag
1684 */
1685 protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
1686 this.useIdentityHashCode = useIdentityHashCode;
1687 }
1688
1689 //---------------------------------------------------------------------
1690
1691 /**
1692 * <p>Gets whether to use the field names passed in.</p>
1693 *
1694 * @return the current useFieldNames flag
1695 */
1696 protected boolean isUseFieldNames() {
1697 return useFieldNames;
1698 }
1699
1700 /**
1701 * <p>Sets whether to use the field names passed in.</p>
1702 *
1703 * @param useFieldNames the new useFieldNames flag
1704 */
1705 protected void setUseFieldNames(final boolean useFieldNames) {
1706 this.useFieldNames = useFieldNames;
1707 }
1708
1709 //---------------------------------------------------------------------
1710
1711 /**
1712 * <p>Gets whether to use full detail when the caller doesn't
1713 * specify.</p>
1714 *
1715 * @return the current defaultFullDetail flag
1716 */
1717 protected boolean isDefaultFullDetail() {
1718 return defaultFullDetail;
1719 }
1720
1721 /**
1722 * <p>Sets whether to use full detail when the caller doesn't
1723 * specify.</p>
1724 *
1725 * @param defaultFullDetail the new defaultFullDetail flag
1726 */
1727 protected void setDefaultFullDetail(final boolean defaultFullDetail) {
1728 this.defaultFullDetail = defaultFullDetail;
1729 }
1730
1731 //---------------------------------------------------------------------
1732
1733 /**
1734 * <p>Gets whether to output array content detail.</p>
1735 *
1736 * @return the current array content detail setting
1737 */
1738 protected boolean isArrayContentDetail() {
1739 return arrayContentDetail;
1740 }
1741
1742 /**
1743 * <p>Sets whether to output array content detail.</p>
1744 *
1745 * @param arrayContentDetail the new arrayContentDetail flag
1746 */
1747 protected void setArrayContentDetail(final boolean arrayContentDetail) {
1748 this.arrayContentDetail = arrayContentDetail;
1749 }
1750
1751 //---------------------------------------------------------------------
1752
1753 /**
1754 * <p>Gets the array start text.</p>
1755 *
1756 * @return the current array start text
1757 */
1758 protected String getArrayStart() {
1759 return arrayStart;
1760 }
1761
1762 /**
1763 * <p>Sets the array start text.</p>
1764 *
1765 * <p>{@code null} is accepted, but will be converted to
1766 * an empty String.</p>
1767 *
1768 * @param arrayStart the new array start text
1769 */
1770 protected void setArrayStart(String arrayStart) {
1771 if (arrayStart == null) {
1772 arrayStart = StringUtils.EMPTY;
1773 }
1774 this.arrayStart = arrayStart;
1775 }
1776
1777 //---------------------------------------------------------------------
1778
1779 /**
1780 * <p>Gets the array end text.</p>
1781 *
1782 * @return the current array end text
1783 */
1784 protected String getArrayEnd() {
1785 return arrayEnd;
1786 }
1787
1788 /**
1789 * <p>Sets the array end text.</p>
1790 *
1791 * <p>{@code null} is accepted, but will be converted to
1792 * an empty String.</p>
1793 *
1794 * @param arrayEnd the new array end text
1795 */
1796 protected void setArrayEnd(String arrayEnd) {
1797 if (arrayEnd == null) {
1798 arrayEnd = StringUtils.EMPTY;
1799 }
1800 this.arrayEnd = arrayEnd;
1801 }
1802
1803 //---------------------------------------------------------------------
1804
1805 /**
1806 * <p>Gets the array separator text.</p>
1807 *
1808 * @return the current array separator text
1809 */
1810 protected String getArraySeparator() {
1811 return arraySeparator;
1812 }
1813
1814 /**
1815 * <p>Sets the array separator text.</p>
1816 *
1817 * <p>{@code null} is accepted, but will be converted to
1818 * an empty String.</p>
1819 *
1820 * @param arraySeparator the new array separator text
1821 */
1822 protected void setArraySeparator(String arraySeparator) {
1823 if (arraySeparator == null) {
1824 arraySeparator = StringUtils.EMPTY;
1825 }
1826 this.arraySeparator = arraySeparator;
1827 }
1828
1829 //---------------------------------------------------------------------
1830
1831 /**
1832 * <p>Gets the content start text.</p>
1833 *
1834 * @return the current content start text
1835 */
1836 protected String getContentStart() {
1837 return contentStart;
1838 }
1839
1840 /**
1841 * <p>Sets the content start text.</p>
1842 *
1843 * <p>{@code null} is accepted, but will be converted to
1844 * an empty String.</p>
1845 *
1846 * @param contentStart the new content start text
1847 */
1848 protected void setContentStart(String contentStart) {
1849 if (contentStart == null) {
1850 contentStart = StringUtils.EMPTY;
1851 }
1852 this.contentStart = contentStart;
1853 }
1854
1855 //---------------------------------------------------------------------
1856
1857 /**
1858 * <p>Gets the content end text.</p>
1859 *
1860 * @return the current content end text
1861 */
1862 protected String getContentEnd() {
1863 return contentEnd;
1864 }
1865
1866 /**
1867 * <p>Sets the content end text.</p>
1868 *
1869 * <p>{@code null} is accepted, but will be converted to
1870 * an empty String.</p>
1871 *
1872 * @param contentEnd the new content end text
1873 */
1874 protected void setContentEnd(String contentEnd) {
1875 if (contentEnd == null) {
1876 contentEnd = StringUtils.EMPTY;
1877 }
1878 this.contentEnd = contentEnd;
1879 }
1880
1881 //---------------------------------------------------------------------
1882
1883 /**
1884 * <p>Gets the field name value separator text.</p>
1885 *
1886 * @return the current field name value separator text
1887 */
1888 protected String getFieldNameValueSeparator() {
1889 return fieldNameValueSeparator;
1890 }
1891
1892 /**
1893 * <p>Sets the field name value separator text.</p>
1894 *
1895 * <p>{@code null} is accepted, but will be converted to
1896 * an empty String.</p>
1897 *
1898 * @param fieldNameValueSeparator the new field name value separator text
1899 */
1900 protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
1901 if (fieldNameValueSeparator == null) {
1902 fieldNameValueSeparator = StringUtils.EMPTY;
1903 }
1904 this.fieldNameValueSeparator = fieldNameValueSeparator;
1905 }
1906
1907 //---------------------------------------------------------------------
1908
1909 /**
1910 * <p>Gets the field separator text.</p>
1911 *
1912 * @return the current field separator text
1913 */
1914 protected String getFieldSeparator() {
1915 return fieldSeparator;
1916 }
1917
1918 /**
1919 * <p>Sets the field separator text.</p>
1920 *
1921 * <p>{@code null} is accepted, but will be converted to
1922 * an empty String.</p>
1923 *
1924 * @param fieldSeparator the new field separator text
1925 */
1926 protected void setFieldSeparator(String fieldSeparator) {
1927 if (fieldSeparator == null) {
1928 fieldSeparator = StringUtils.EMPTY;
1929 }
1930 this.fieldSeparator = fieldSeparator;
1931 }
1932
1933 //---------------------------------------------------------------------
1934
1935 /**
1936 * <p>Gets whether the field separator should be added at the start
1937 * of each buffer.</p>
1938 *
1939 * @return the fieldSeparatorAtStart flag
1940 * @since 2.0
1941 */
1942 protected boolean isFieldSeparatorAtStart() {
1943 return fieldSeparatorAtStart;
1944 }
1945
1946 /**
1947 * <p>Sets whether the field separator should be added at the start
1948 * of each buffer.</p>
1949 *
1950 * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
1951 * @since 2.0
1952 */
1953 protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
1954 this.fieldSeparatorAtStart = fieldSeparatorAtStart;
1955 }
1956
1957 //---------------------------------------------------------------------
1958
1959 /**
1960 * <p>Gets whether the field separator should be added at the end
1961 * of each buffer.</p>
1962 *
1963 * @return fieldSeparatorAtEnd flag
1964 * @since 2.0
1965 */
1966 protected boolean isFieldSeparatorAtEnd() {
1967 return fieldSeparatorAtEnd;
1968 }
1969
1970 /**
1971 * <p>Sets whether the field separator should be added at the end
1972 * of each buffer.</p>
1973 *
1974 * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
1975 * @since 2.0
1976 */
1977 protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
1978 this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
1979 }
1980
1981 //---------------------------------------------------------------------
1982
1983 /**
1984 * <p>Gets the text to output when {@code null} found.</p>
1985 *
1986 * @return the current text to output when null found
1987 */
1988 protected String getNullText() {
1989 return nullText;
1990 }
1991
1992 /**
1993 * <p>Sets the text to output when {@code null} found.</p>
1994 *
1995 * <p>{@code null} is accepted, but will be converted to
1996 * an empty String.</p>
1997 *
1998 * @param nullText the new text to output when null found
1999 */
2000 protected void setNullText(String nullText) {
2001 if (nullText == null) {
2002 nullText = StringUtils.EMPTY;
2003 }
2004 this.nullText = nullText;
2005 }
2006
2007 //---------------------------------------------------------------------
2008
2009 /**
2010 * <p>Gets the start text to output when a {@code Collection},
2011 * {@code Map} or array size is output.</p>
2012 *
2013 * <p>This is output before the size value.</p>
2014 *
2015 * @return the current start of size text
2016 */
2017 protected String getSizeStartText() {
2018 return sizeStartText;
2019 }
2020
2021 /**
2022 * <p>Sets the start text to output when a {@code Collection},
2023 * {@code Map} or array size is output.</p>
2024 *
2025 * <p>This is output before the size value.</p>
2026 *
2027 * <p>{@code null} is accepted, but will be converted to
2028 * an empty String.</p>
2029 *
2030 * @param sizeStartText the new start of size text
2031 */
2032 protected void setSizeStartText(String sizeStartText) {
2033 if (sizeStartText == null) {
2034 sizeStartText = StringUtils.EMPTY;
2035 }
2036 this.sizeStartText = sizeStartText;
2037 }
2038
2039 //---------------------------------------------------------------------
2040
2041 /**
2042 * <p>Gets the end text to output when a {@code Collection},
2043 * {@code Map} or array size is output.</p>
2044 *
2045 * <p>This is output after the size value.</p>
2046 *
2047 * @return the current end of size text
2048 */
2049 protected String getSizeEndText() {
2050 return sizeEndText;
2051 }
2052
2053 /**
2054 * <p>Sets the end text to output when a {@code Collection},
2055 * {@code Map} or array size is output.</p>
2056 *
2057 * <p>This is output after the size value.</p>
2058 *
2059 * <p>{@code null} is accepted, but will be converted to
2060 * an empty String.</p>
2061 *
2062 * @param sizeEndText the new end of size text
2063 */
2064 protected void setSizeEndText(String sizeEndText) {
2065 if (sizeEndText == null) {
2066 sizeEndText = StringUtils.EMPTY;
2067 }
2068 this.sizeEndText = sizeEndText;
2069 }
2070
2071 //---------------------------------------------------------------------
2072
2073 /**
2074 * <p>Gets the start text to output when an {@code Object} is
2075 * output in summary mode.</p>
2076 *
2077 * <p>This is output before the size value.</p>
2078 *
2079 * @return the current start of summary text
2080 */
2081 protected String getSummaryObjectStartText() {
2082 return summaryObjectStartText;
2083 }
2084
2085 /**
2086 * <p>Sets the start text to output when an {@code Object} is
2087 * output in summary mode.</p>
2088 *
2089 * <p>This is output before the size value.</p>
2090 *
2091 * <p>{@code null} is accepted, but will be converted to
2092 * an empty String.</p>
2093 *
2094 * @param summaryObjectStartText the new start of summary text
2095 */
2096 protected void setSummaryObjectStartText(String summaryObjectStartText) {
2097 if (summaryObjectStartText == null) {
2098 summaryObjectStartText = StringUtils.EMPTY;
2099 }
2100 this.summaryObjectStartText = summaryObjectStartText;
2101 }
2102
2103 //---------------------------------------------------------------------
2104
2105 /**
2106 * <p>Gets the end text to output when an {@code Object} is
2107 * output in summary mode.</p>
2108 *
2109 * <p>This is output after the size value.</p>
2110 *
2111 * @return the current end of summary text
2112 */
2113 protected String getSummaryObjectEndText() {
2114 return summaryObjectEndText;
2115 }
2116
2117 /**
2118 * <p>Sets the end text to output when an {@code Object} is
2119 * output in summary mode.</p>
2120 *
2121 * <p>This is output after the size value.</p>
2122 *
2123 * <p>{@code null} is accepted, but will be converted to
2124 * an empty String.</p>
2125 *
2126 * @param summaryObjectEndText the new end of summary text
2127 */
2128 protected void setSummaryObjectEndText(String summaryObjectEndText) {
2129 if (summaryObjectEndText == null) {
2130 summaryObjectEndText = StringUtils.EMPTY;
2131 }
2132 this.summaryObjectEndText = summaryObjectEndText;
2133 }
2134
2135 //----------------------------------------------------------------------------
2136
2137 /**
2138 * <p>Default {@code ToStringStyle}.</p>
2139 *
2140 * <p>This is an inner class rather than using
2141 * {@code StandardToStringStyle} to ensure its immutability.</p>
2142 */
2143 private static final class DefaultToStringStyle extends ToStringStyle {
2144
2145 /**
2146 * Required for serialization support.
2147 *
2148 * @see java.io.Serializable
2149 */
2150 private static final long serialVersionUID = 1L;
2151
2152 /**
2153 * <p>Constructor.</p>
2154 *
2155 * <p>Use the static constant rather than instantiating.</p>
2156 */
2157 DefaultToStringStyle() {
2158 super();
2159 }
2160
2161 /**
2162 * <p>Ensure {@code Singleton} after serialization.</p>
2163 *
2164 * @return the singleton
2165 */
2166 private Object readResolve() {
2167 return DEFAULT_STYLE;
2168 }
2169
2170 }
2171
2172 //----------------------------------------------------------------------------
2173
2174 /**
2175 * <p>{@code ToStringStyle} that does not print out
2176 * the field names.</p>
2177 *
2178 * <p>This is an inner class rather than using
2179 * {@code StandardToStringStyle} to ensure its immutability.
2180 */
2181 private static final class NoFieldNameToStringStyle extends ToStringStyle {
2182
2183 private static final long serialVersionUID = 1L;
2184
2185 /**
2186 * <p>Constructor.</p>
2187 *
2188 * <p>Use the static constant rather than instantiating.</p>
2189 */
2190 NoFieldNameToStringStyle() {
2191 super();
2192 this.setUseFieldNames(false);
2193 }
2194
2195 /**
2196 * <p>Ensure {@code Singleton} after serialization.</p>
2197 *
2198 * @return the singleton
2199 */
2200 private Object readResolve() {
2201 return NO_FIELD_NAMES_STYLE;
2202 }
2203
2204 }
2205
2206 //----------------------------------------------------------------------------
2207
2208 /**
2209 * <p>{@code ToStringStyle} that prints out the short
2210 * class name and no identity hashcode.</p>
2211 *
2212 * <p>This is an inner class rather than using
2213 * {@code StandardToStringStyle} to ensure its immutability.</p>
2214 */
2215 private static final class ShortPrefixToStringStyle extends ToStringStyle {
2216
2217 private static final long serialVersionUID = 1L;
2218
2219 /**
2220 * <p>Constructor.</p>
2221 *
2222 * <p>Use the static constant rather than instantiating.</p>
2223 */
2224 ShortPrefixToStringStyle() {
2225 super();
2226 this.setUseShortClassName(true);
2227 this.setUseIdentityHashCode(false);
2228 }
2229
2230 /**
2231 * <p>Ensure <code>Singleton</ode> after serialization.</p>
2232 * @return the singleton
2233 */
2234 private Object readResolve() {
2235 return SHORT_PREFIX_STYLE;
2236 }
2237
2238 }
2239
2240 //----------------------------------------------------------------------------
2241
2242 /**
2243 * <p>{@code ToStringStyle} that does not print out the
2244 * classname, identity hashcode, content start or field name.</p>
2245 *
2246 * <p>This is an inner class rather than using
2247 * {@code StandardToStringStyle} to ensure its immutability.</p>
2248 */
2249 private static final class SimpleToStringStyle extends ToStringStyle {
2250
2251 private static final long serialVersionUID = 1L;
2252
2253 /**
2254 * <p>Constructor.</p>
2255 *
2256 * <p>Use the static constant rather than instantiating.</p>
2257 */
2258 SimpleToStringStyle() {
2259 super();
2260 this.setUseClassName(false);
2261 this.setUseIdentityHashCode(false);
2262 this.setUseFieldNames(false);
2263 this.setContentStart(StringUtils.EMPTY);
2264 this.setContentEnd(StringUtils.EMPTY);
2265 }
2266
2267 /**
2268 * <p>Ensure <code>Singleton</ode> after serialization.</p>
2269 * @return the singleton
2270 */
2271 private Object readResolve() {
2272 return SIMPLE_STYLE;
2273 }
2274
2275 }
2276
2277 //----------------------------------------------------------------------------
2278
2279 /**
2280 * <p>{@code ToStringStyle} that outputs on multiple lines.</p>
2281 *
2282 * <p>This is an inner class rather than using
2283 * {@code StandardToStringStyle} to ensure its immutability.</p>
2284 */
2285 private static final class MultiLineToStringStyle extends ToStringStyle {
2286
2287 private static final long serialVersionUID = 1L;
2288
2289 /**
2290 * <p>Constructor.</p>
2291 *
2292 * <p>Use the static constant rather than instantiating.</p>
2293 */
2294 MultiLineToStringStyle() {
2295 super();
2296 this.setContentStart("[");
2297 this.setFieldSeparator(System.lineSeparator() + " ");
2298 this.setFieldSeparatorAtStart(true);
2299 this.setContentEnd(System.lineSeparator() + "]");
2300 }
2301
2302 /**
2303 * <p>Ensure {@code Singleton} after serialization.</p>
2304 *
2305 * @return the singleton
2306 */
2307 private Object readResolve() {
2308 return MULTI_LINE_STYLE;
2309 }
2310
2311 }
2312
2313 //----------------------------------------------------------------------------
2314
2315 /**
2316 * <p>{@code ToStringStyle} that does not print out the classname
2317 * and identity hash code but prints content start and field names.</p>
2318 *
2319 * <p>This is an inner class rather than using
2320 * {@code StandardToStringStyle} to ensure its immutability.</p>
2321 */
2322 private static final class NoClassNameToStringStyle extends ToStringStyle {
2323
2324 private static final long serialVersionUID = 1L;
2325
2326 /**
2327 * <p>Constructor.</p>
2328 *
2329 * <p>Use the static constant rather than instantiating.</p>
2330 */
2331 NoClassNameToStringStyle() {
2332 super();
2333 this.setUseClassName(false);
2334 this.setUseIdentityHashCode(false);
2335 }
2336
2337 /**
2338 * <p>Ensure {@code Singleton} after serialization.</p>
2339 *
2340 * @return the singleton
2341 */
2342 private Object readResolve() {
2343 return NO_CLASS_NAME_STYLE;
2344 }
2345
2346 }
2347
2348 // ----------------------------------------------------------------------------
2349
2350 /**
2351 * <p>
2352 * {@code ToStringStyle} that outputs with JSON format.
2353 * </p>
2354 *
2355 * <p>
2356 * This is an inner class rather than using
2357 * {@code StandardToStringStyle} to ensure its immutability.
2358 * </p>
2359 *
2360 * @since 3.4
2361 * @see <a href="http://json.org">json.org</a>
2362 */
2363 private static final class JsonToStringStyle extends ToStringStyle {
2364
2365 private static final long serialVersionUID = 1L;
2366
2367 private static final String FIELD_NAME_QUOTE = "\"";
2368
2369 /**
2370 * <p>
2371 * Constructor.
2372 * </p>
2373 *
2374 * <p>
2375 * Use the static constant rather than instantiating.
2376 * </p>
2377 */
2378 JsonToStringStyle() {
2379 super();
2380
2381 this.setUseClassName(false);
2382 this.setUseIdentityHashCode(false);
2383
2384 this.setContentStart("{");
2385 this.setContentEnd("}");
2386
2387 this.setArrayStart("[");
2388 this.setArrayEnd("]");
2389
2390 this.setFieldSeparator(",");
2391 this.setFieldNameValueSeparator(":");
2392
2393 this.setNullText("null");
2394
2395 this.setSummaryObjectStartText("\"<");
2396 this.setSummaryObjectEndText(">\"");
2397
2398 this.setSizeStartText("\"<size=");
2399 this.setSizeEndText(">\"");
2400 }
2401
2402 @Override
2403 public void append(final StringBuffer buffer, final String fieldName,
2404 final Object[] array, final Boolean fullDetail) {
2405
2406 if (fieldName == null) {
2407 throw new UnsupportedOperationException(
2408 "Field names are mandatory when using JsonToStringStyle");
2409 }
2410 if (!isFullDetail(fullDetail)) {
2411 throw new UnsupportedOperationException(
2412 "FullDetail must be true when using JsonToStringStyle");
2413 }
2414
2415 super.append(buffer, fieldName, array, fullDetail);
2416 }
2417
2418 @Override
2419 public void append(final StringBuffer buffer, final String fieldName, final long[] array,
2420 final Boolean fullDetail) {
2421
2422 if (fieldName == null) {
2423 throw new UnsupportedOperationException(
2424 "Field names are mandatory when using JsonToStringStyle");
2425 }
2426 if (!isFullDetail(fullDetail)) {
2427 throw new UnsupportedOperationException(
2428 "FullDetail must be true when using JsonToStringStyle");
2429 }
2430
2431 super.append(buffer, fieldName, array, fullDetail);
2432 }
2433
2434 @Override
2435 public void append(final StringBuffer buffer, final String fieldName, final int[] array,
2436 final Boolean fullDetail) {
2437
2438 if (fieldName == null) {
2439 throw new UnsupportedOperationException(
2440 "Field names are mandatory when using JsonToStringStyle");
2441 }
2442 if (!isFullDetail(fullDetail)) {
2443 throw new UnsupportedOperationException(
2444 "FullDetail must be true when using JsonToStringStyle");
2445 }
2446
2447 super.append(buffer, fieldName, array, fullDetail);
2448 }
2449
2450 @Override
2451 public void append(final StringBuffer buffer, final String fieldName,
2452 final short[] array, final Boolean fullDetail) {
2453
2454 if (fieldName == null) {
2455 throw new UnsupportedOperationException(
2456 "Field names are mandatory when using JsonToStringStyle");
2457 }
2458 if (!isFullDetail(fullDetail)) {
2459 throw new UnsupportedOperationException(
2460 "FullDetail must be true when using JsonToStringStyle");
2461 }
2462
2463 super.append(buffer, fieldName, array, fullDetail);
2464 }
2465
2466 @Override
2467 public void append(final StringBuffer buffer, final String fieldName, final byte[] array,
2468 final Boolean fullDetail) {
2469
2470 if (fieldName == null) {
2471 throw new UnsupportedOperationException(
2472 "Field names are mandatory when using JsonToStringStyle");
2473 }
2474 if (!isFullDetail(fullDetail)) {
2475 throw new UnsupportedOperationException(
2476 "FullDetail must be true when using JsonToStringStyle");
2477 }
2478
2479 super.append(buffer, fieldName, array, fullDetail);
2480 }
2481
2482 @Override
2483 public void append(final StringBuffer buffer, final String fieldName, final char[] array,
2484 final Boolean fullDetail) {
2485
2486 if (fieldName == null) {
2487 throw new UnsupportedOperationException(
2488 "Field names are mandatory when using JsonToStringStyle");
2489 }
2490 if (!isFullDetail(fullDetail)) {
2491 throw new UnsupportedOperationException(
2492 "FullDetail must be true when using JsonToStringStyle");
2493 }
2494
2495 super.append(buffer, fieldName, array, fullDetail);
2496 }
2497
2498 @Override
2499 public void append(final StringBuffer buffer, final String fieldName,
2500 final double[] array, final Boolean fullDetail) {
2501
2502 if (fieldName == null) {
2503 throw new UnsupportedOperationException(
2504 "Field names are mandatory when using JsonToStringStyle");
2505 }
2506 if (!isFullDetail(fullDetail)) {
2507 throw new UnsupportedOperationException(
2508 "FullDetail must be true when using JsonToStringStyle");
2509 }
2510
2511 super.append(buffer, fieldName, array, fullDetail);
2512 }
2513
2514 @Override
2515 public void append(final StringBuffer buffer, final String fieldName,
2516 final float[] array, final Boolean fullDetail) {
2517
2518 if (fieldName == null) {
2519 throw new UnsupportedOperationException(
2520 "Field names are mandatory when using JsonToStringStyle");
2521 }
2522 if (!isFullDetail(fullDetail)) {
2523 throw new UnsupportedOperationException(
2524 "FullDetail must be true when using JsonToStringStyle");
2525 }
2526
2527 super.append(buffer, fieldName, array, fullDetail);
2528 }
2529
2530 @Override
2531 public void append(final StringBuffer buffer, final String fieldName,
2532 final boolean[] array, final Boolean fullDetail) {
2533
2534 if (fieldName == null) {
2535 throw new UnsupportedOperationException(
2536 "Field names are mandatory when using JsonToStringStyle");
2537 }
2538 if (!isFullDetail(fullDetail)) {
2539 throw new UnsupportedOperationException(
2540 "FullDetail must be true when using JsonToStringStyle");
2541 }
2542
2543 super.append(buffer, fieldName, array, fullDetail);
2544 }
2545
2546 @Override
2547 public void append(final StringBuffer buffer, final String fieldName, final Object value,
2548 final Boolean fullDetail) {
2549
2550 if (fieldName == null) {
2551 throw new UnsupportedOperationException(
2552 "Field names are mandatory when using JsonToStringStyle");
2553 }
2554 if (!isFullDetail(fullDetail)) {
2555 throw new UnsupportedOperationException(
2556 "FullDetail must be true when using JsonToStringStyle");
2557 }
2558
2559 super.append(buffer, fieldName, value, fullDetail);
2560 }
2561
2562 @Override
2563 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
2564 appendValueAsString(buffer, String.valueOf(value));
2565 }
2566
2567 @Override
2568 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
2569
2570 if (value == null) {
2571 appendNullText(buffer, fieldName);
2572 return;
2573 }
2574
2575 if (value instanceof String || value instanceof Character) {
2576 appendValueAsString(buffer, value.toString());
2577 return;
2578 }
2579
2580 if (value instanceof Number || value instanceof Boolean) {
2581 buffer.append(value);
2582 return;
2583 }
2584
2585 final String valueAsString = value.toString();
2586 if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
2587 buffer.append(value);
2588 return;
2589 }
2590
2591 appendDetail(buffer, fieldName, valueAsString);
2592 }
2593
2594 private boolean isJsonArray(final String valueAsString) {
2595 return valueAsString.startsWith(getArrayStart())
2596 && valueAsString.endsWith(getArrayEnd());
2597 }
2598
2599 private boolean isJsonObject(final String valueAsString) {
2600 return valueAsString.startsWith(getContentStart())
2601 && valueAsString.endsWith(getContentEnd());
2602 }
2603
2604 /**
2605 * Appends the given String enclosed in double-quotes to the given StringBuffer.
2606 *
2607 * @param buffer the StringBuffer to append the value to.
2608 * @param value the value to append.
2609 */
2610 private void appendValueAsString(final StringBuffer buffer, final String value) {
2611 buffer.append('"').append(StringEscapeUtils.escapeJson(value)).append('"');
2612 }
2613
2614 @Override
2615 protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
2616
2617 if (fieldName == null) {
2618 throw new UnsupportedOperationException(
2619 "Field names are mandatory when using JsonToStringStyle");
2620 }
2621
2622 super.appendFieldStart(buffer, FIELD_NAME_QUOTE + StringEscapeUtils.escapeJson(fieldName)
2623 + FIELD_NAME_QUOTE);
2624 }
2625
2626 /**
2627 * <p>
2628 * Ensure {@code Singleton} after serialization.
2629 * </p>
2630 *
2631 * @return the singleton
2632 */
2633 private Object readResolve() {
2634 return JSON_STYLE;
2635 }
2636
2637 }
2638 }
2639