'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+/**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest
+ * the detail level requested
+ * @return whether full detail is to be shown
+ */
+protected boolean isFullDetail(final java.lang.Boolean fullDetailRequest) {
+ {
+ return /* NPEX_NULL_EXP */
+ fullDetailRequest.booleanValue();
+ }
+}
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_1605/metadata.json b/Java/commons-lang-ToStringStyle_1605/metadata.json
new file mode 100644
index 000000000..a1e02d9bd
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_1605/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_1605",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 1622,
+ "npe_method": "isFullDetail",
+ "deref_field": "fullDetailRequest",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_1605"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_1605/npe.json b/Java/commons-lang-ToStringStyle_1605/npe.json
new file mode 100644
index 000000000..f191cbdb2
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_1605/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 1622,
+ "npe_method": "isFullDetail",
+ "deref_field": "fullDetailRequest",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_241/Dockerfile b/Java/commons-lang-ToStringStyle_241/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_241/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_241/buggy.java b/Java/commons-lang-ToStringStyle_241/buggy.java
new file mode 100644
index 000000000..1fd7f49b9
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_241/buggy.java
@@ -0,0 +1,2655 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+/**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+static void unregister(final java.lang.Object value) {
+ {
+ final java.util.Map m = org.apache.commons.lang3.builder.ToStringStyle.getRegistry();
+ {
+ /* NPEX_NULL_EXP */
+ m.remove(/* NPEX_NULL_EXP */
+ value);
+ if (m.isEmpty()) {
+ org.apache.commons.lang3.builder.ToStringStyle.REGISTRY.remove();
+ }
+ }
+ }
+}
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_241/metadata.json b/Java/commons-lang-ToStringStyle_241/metadata.json
new file mode 100644
index 000000000..31f871130
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_241/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_241",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 256,
+ "npe_method": "unregister",
+ "deref_field": "m",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_241"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_241/npe.json b/Java/commons-lang-ToStringStyle_241/npe.json
new file mode 100644
index 000000000..5c2bbc5a5
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_241/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 256,
+ "npe_method": "unregister",
+ "deref_field": "m",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_2620/Dockerfile b/Java/commons-lang-ToStringStyle_2620/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_2620/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_2620/buggy.java b/Java/commons-lang-ToStringStyle_2620/buggy.java
new file mode 100644
index 000000000..8a1881a05
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_2620/buggy.java
@@ -0,0 +1,2637 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+protected void appendFieldStart(java.lang.StringBuffer buffer, java.lang.String fieldName) {
+ {
+ super.appendFieldStart(buffer, (FIELD_NAME_PREFIX + /* NPEX_NULL_EXP */
+ fieldName) + FIELD_NAME_PREFIX);
+ }
+}
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_2620/metadata.json b/Java/commons-lang-ToStringStyle_2620/metadata.json
new file mode 100644
index 000000000..0370eb269
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_2620/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_2620",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 2621,
+ "npe_method": "appendFieldStart",
+ "deref_field": "fieldName",
+ "npe_class": "JsonToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_2620"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_2620/npe.json b/Java/commons-lang-ToStringStyle_2620/npe.json
new file mode 100644
index 000000000..bfa44211e
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_2620/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 2621,
+ "npe_method": "appendFieldStart",
+ "deref_field": "fieldName",
+ "npe_class": "JsonToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_408/Dockerfile b/Java/commons-lang-ToStringStyle_408/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_408/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_408/buggy.java b/Java/commons-lang-ToStringStyle_408/buggy.java
new file mode 100644
index 000000000..4ce7394d7
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_408/buggy.java
@@ -0,0 +1,2650 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+/**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer
+ * the StringBuffer
to populate
+ * @param object
+ * the Object
to build a toString
for
+ */
+public void appendStart(final java.lang.StringBuffer buffer, final java.lang.Object object) {
+ {
+ appendClassName(buffer, /* NPEX_NULL_EXP */
+ object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+}
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_408/metadata.json b/Java/commons-lang-ToStringStyle_408/metadata.json
new file mode 100644
index 000000000..5d17e3d10
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_408/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_408",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 418,
+ "npe_method": "appendStart",
+ "deref_field": "object",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_408"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_408/npe.json b/Java/commons-lang-ToStringStyle_408/npe.json
new file mode 100644
index 000000000..4022745e9
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_408/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 418,
+ "npe_method": "appendStart",
+ "deref_field": "object",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_472/Dockerfile b/Java/commons-lang-ToStringStyle_472/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_472/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_472/buggy.java b/Java/commons-lang-ToStringStyle_472/buggy.java
new file mode 100644
index 000000000..d0b95a10b
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_472/buggy.java
@@ -0,0 +1,2653 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+// ----------------------------------------------------------------------------
+/**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer
+ * the StringBuffer
to populate
+ * @param fieldName
+ * the field name
+ * @param value
+ * the value to add to the toString
+ * @param fullDetail
+ * true
for detail, false
+ * for summary info, null
for style decides
+ */
+public void append(final java.lang.StringBuffer buffer, final java.lang.String fieldName, final java.lang.Object value, final java.lang.Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+ {
+ appendInternal(buffer, fieldName, /* NPEX_NULL_EXP */
+ value, isFullDetail(fullDetail));
+ }
+ appendFieldEnd(buffer, fieldName);
+}
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_472/metadata.json b/Java/commons-lang-ToStringStyle_472/metadata.json
new file mode 100644
index 000000000..133591bd1
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_472/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_472",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 489,
+ "npe_method": "append",
+ "deref_field": "value",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_472"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_472/npe.json b/Java/commons-lang-ToStringStyle_472/npe.json
new file mode 100644
index 000000000..4d6e021fe
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_472/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 489,
+ "npe_method": "append",
+ "deref_field": "value",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_905/Dockerfile b/Java/commons-lang-ToStringStyle_905/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_905/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_905/buggy.java b/Java/commons-lang-ToStringStyle_905/buggy.java
new file mode 100644
index 000000000..5f388fb13
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_905/buggy.java
@@ -0,0 +1,2652 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+/**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer
+ * the StringBuffer
to populate
+ * @param fieldName
+ * the field name
+ * @param array
+ * the array to add to the toString
+ * @param fullDetail
+ * true
for detail, false
+ * for summary info, null
for style decides
+ */
+public void append(final java.lang.StringBuffer buffer, final java.lang.String fieldName, final java.lang.Object[] array, final java.lang.Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+ {
+ if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, /* NPEX_NULL_EXP */
+ array);
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+ }
+ appendFieldEnd(buffer, fieldName);
+}
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_905/metadata.json b/Java/commons-lang-ToStringStyle_905/metadata.json
new file mode 100644
index 000000000..b74d7299c
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_905/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_905",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 921,
+ "npe_method": "append",
+ "deref_field": "array",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_905"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_905/npe.json b/Java/commons-lang-ToStringStyle_905/npe.json
new file mode 100644
index 000000000..573d45986
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_905/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 921,
+ "npe_method": "append",
+ "deref_field": "array",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_936/Dockerfile b/Java/commons-lang-ToStringStyle_936/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_936/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_936/buggy.java b/Java/commons-lang-ToStringStyle_936/buggy.java
new file mode 100644
index 000000000..104813ce2
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_936/buggy.java
@@ -0,0 +1,2652 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+// ----------------------------------------------------------------------------
+/**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer
+ * the StringBuffer
to populate
+ * @param fieldName
+ * the field name, typically not used as already appended
+ * @param array
+ * the array to add to the toString
,
+ * not null
+ */
+protected void appendDetail(final java.lang.StringBuffer buffer, final java.lang.String fieldName, final java.lang.Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final java.lang.Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ {
+ appendInternal(buffer, fieldName, /* NPEX_NULL_EXP */
+ item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+}
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+ protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
+ buffer.append(arrayStart);
+ final int length = Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_936/metadata.json b/Java/commons-lang-ToStringStyle_936/metadata.json
new file mode 100644
index 000000000..891435fc4
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_936/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_936",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 951,
+ "npe_method": "appendDetail",
+ "deref_field": "item",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_936"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_936/npe.json b/Java/commons-lang-ToStringStyle_936/npe.json
new file mode 100644
index 000000000..e6b8786eb
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_936/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 951,
+ "npe_method": "appendDetail",
+ "deref_field": "item",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-ToStringStyle_963/Dockerfile b/Java/commons-lang-ToStringStyle_963/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_963/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-ToStringStyle_963/buggy.java b/Java/commons-lang-ToStringStyle_963/buggy.java
new file mode 100644
index 000000000..2ea6d9498
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_963/buggy.java
@@ -0,0 +1,2650 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.builder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * Controls String
formatting for {@link ToStringBuilder}.
+ * The main public interface is always via ToStringBuilder
.
+ *
+ * These classes are intended to be used as Singletons
.
+ * There is no need to instantiate a new style each time. A program
+ * will generally use one of the predefined constants on this class.
+ * Alternatively, the {@link StandardToStringStyle} class can be used
+ * to set the individual settings. Thus most styles can be achieved
+ * without subclassing.
+ *
+ * If required, a subclass can override as many or as few of the
+ * methods as it requires. Each object type (from boolean
+ * to long
to Object
to int[]
) has
+ * its own methods to output it. Most have two versions, detail and summary.
+ *
+ *
For example, the detail version of the array based methods will
+ * output the whole array, whereas the summary method will just output
+ * the array length.
+ *
+ * If you want to format the output of certain objects, such as dates, you
+ * must create a subclass and override a method.
+ *
+ *
+ * public class MyStyle extends ToStringStyle {
+ * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ * if (value instanceof Date) {
+ * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ * }
+ * buffer.append(value);
+ * }
+ * }
+ *
+ *
+ * @since 1.0
+ */
+public abstract class ToStringStyle implements Serializable {
+
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = -2587890625525655916L;
+
+ /**
+ * The default toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[name=John Doe,age=33,smoker=false]
+ *
+ */
+ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
+
+ /**
+ * The multi line toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person@182f0db[
+ * name=John Doe
+ * age=33
+ * smoker=false
+ * ]
+ *
+ */
+ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
+
+ /**
+ * The no field names toString style. Using the
+ * Person
example from {@link ToStringBuilder}, the output
+ * would look like this:
+ *
+ *
+ * Person@182f0db[John Doe,33,false]
+ *
+ */
+ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
+
+ /**
+ * The short prefix toString style. Using the Person
example
+ * from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * Person[name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 2.1
+ */
+ public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
+
+ /**
+ * The simple toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * John Doe,33,false
+ *
+ */
+ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
+
+ /**
+ * The no class name toString style. Using the Person
+ * example from {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * [name=John Doe,age=33,smoker=false]
+ *
+ *
+ * @since 3.4
+ */
+ public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
+
+ /**
+ * The JSON toString style. Using the Person
example from
+ * {@link ToStringBuilder}, the output would look like this:
+ *
+ *
+ * {"name": "John Doe", "age": 33, "smoker": true}
+ *
+ *
+ * Note: Since field names are mandatory in JSON, this
+ * ToStringStyle will throw an {@link UnsupportedOperationException} if no
+ * field name is passed in while appending. Furthermore This ToStringStyle
+ * will only generate valid JSON if referenced objects also produce JSON
+ * when calling {@code toString()} on them.
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
+
+ /**
+ *
+ * A registry of objects used by reflectionToString
methods
+ * to detect cyclical object references and avoid infinite loops.
+ *
+ */
+ private static final ThreadLocal> REGISTRY =
+ new ThreadLocal>();
+ /*
+ * Note that objects of this class are generally shared between threads, so
+ * an instance variable would not be suitable here.
+ *
+ * In normal use the registry should always be left empty, because the caller
+ * should call toString() which will clean up.
+ *
+ * See LANG-792
+ */
+
+ /**
+ *
+ * Returns the registry of objects being traversed by the reflectionToString
+ * methods in the current thread.
+ *
+ *
+ * @return Set the registry of objects being traversed
+ */
+ static Map getRegistry() {
+ return REGISTRY.get();
+ }
+
+ /**
+ *
+ * Returns true
if the registry contains the given object.
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to lookup in the registry.
+ * @return boolean true
if the registry contains the given
+ * object.
+ */
+ static boolean isRegistered(final Object value) {
+ final Map m = getRegistry();
+ return m != null && m.containsKey(value);
+ }
+
+ /**
+ *
+ * Registers the given object. Used by the reflection methods to avoid
+ * infinite loops.
+ *
+ *
+ * @param value
+ * The object to register.
+ */
+ static void register(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m == null) {
+ REGISTRY.set(new WeakHashMap());
+ }
+ getRegistry().put(value, null);
+ }
+ }
+
+ /**
+ *
+ * Unregisters the given object.
+ *
+ *
+ *
+ * Used by the reflection methods to avoid infinite loops.
+ *
+ *
+ * @param value
+ * The object to unregister.
+ */
+ static void unregister(final Object value) {
+ if (value != null) {
+ final Map m = getRegistry();
+ if (m != null) {
+ m.remove(value);
+ if (m.isEmpty()) {
+ REGISTRY.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether to use the field names, the default is true
.
+ */
+ private boolean useFieldNames = true;
+
+ /**
+ * Whether to use the class name, the default is true
.
+ */
+ private boolean useClassName = true;
+
+ /**
+ * Whether to use short class names, the default is false
.
+ */
+ private boolean useShortClassName = false;
+
+ /**
+ * Whether to use the identity hash code, the default is true
.
+ */
+ private boolean useIdentityHashCode = true;
+
+ /**
+ * The content start '['
.
+ */
+ private String contentStart = "[";
+
+ /**
+ * The content end ']'
.
+ */
+ private String contentEnd = "]";
+
+ /**
+ * The field name value separator '='
.
+ */
+ private String fieldNameValueSeparator = "=";
+
+ /**
+ * Whether the field separator should be added before any other fields.
+ */
+ private boolean fieldSeparatorAtStart = false;
+
+ /**
+ * Whether the field separator should be added after any other fields.
+ */
+ private boolean fieldSeparatorAtEnd = false;
+
+ /**
+ * The field separator ','
.
+ */
+ private String fieldSeparator = ",";
+
+ /**
+ * The array start '{'
.
+ */
+ private String arrayStart = "{";
+
+ /**
+ * The array separator ','
.
+ */
+ private String arraySeparator = ",";
+
+ /**
+ * The detail for array content.
+ */
+ private boolean arrayContentDetail = true;
+
+ /**
+ * The array end '}'
.
+ */
+ private String arrayEnd = "}";
+
+ /**
+ * The value to use when fullDetail is null
,
+ * the default value is true
.
+ */
+ private boolean defaultFullDetail = true;
+
+ /**
+ * The null
text '<null>'
.
+ */
+ private String nullText = "";
+
+ /**
+ * The summary size text start '<size'
.
+ */
+ private String sizeStartText = "'>'.
+ */
+ private String sizeEndText = ">";
+
+ /**
+ * The summary object text start '<'
.
+ */
+ private String summaryObjectStartText = "<";
+
+ /**
+ * The summary object text start '>'
.
+ */
+ private String summaryObjectEndText = ">";
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Constructor.
+ */
+ protected ToStringStyle() {
+ super();
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the superclass toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
superToString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param superToString the super.toString()
+ * @since 2.0
+ */
+ public void appendSuper(final StringBuffer buffer, final String superToString) {
+ appendToString(buffer, superToString);
+ }
+
+ /**
+ * Append to the toString
another toString.
+ * NOTE: It assumes that the toString has been created from the same ToStringStyle.
+ *
+ * A null
toString
is ignored.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param toString the additional toString
+ * @since 2.0
+ */
+ public void appendToString(final StringBuffer buffer, final String toString) {
+ if (toString != null) {
+ final int pos1 = toString.indexOf(contentStart) + contentStart.length();
+ final int pos2 = toString.lastIndexOf(contentEnd);
+ if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
+ final String data = toString.substring(pos1, pos2);
+ if (fieldSeparatorAtStart) {
+ removeLastFieldSeparator(buffer);
+ }
+ buffer.append(data);
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the start of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a toString
for
+ */
+ public void appendStart(final StringBuffer buffer, final Object object) {
+ if (object != null) {
+ appendClassName(buffer, object);
+ appendIdentityHashCode(buffer, object);
+ appendContentStart(buffer);
+ if (fieldSeparatorAtStart) {
+ appendFieldSeparator(buffer);
+ }
+ }
+ }
+
+ /**
+ * Append to the toString
the end of data indicator.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
to build a
+ * toString
for.
+ */
+ public void appendEnd(final StringBuffer buffer, final Object object) {
+ if (this.fieldSeparatorAtEnd == false) {
+ removeLastFieldSeparator(buffer);
+ }
+ appendContentEnd(buffer);
+ unregister(object);
+ }
+
+ /**
+ * Remove the last field separator from the buffer.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @since 2.0
+ */
+ protected void removeLastFieldSeparator(final StringBuffer buffer) {
+ final int len = buffer.length();
+ final int sepLen = fieldSeparator.length();
+ if (len > 0 && sepLen > 0 && len >= sepLen) {
+ boolean match = true;
+ for (int i = 0; i < sepLen; i++) {
+ if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ buffer.setLength(len - sepLen);
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full toString
of the
+ * Object
passed in.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an Object
,
+ * correctly interpreting its type.
+ *
+ * This method performs the main lookup by Class type to correctly
+ * route arrays, Collections
, Maps
and
+ * Objects
to the appropriate method.
+ *
+ * Either detail or summary views can be specified.
+ *
+ * If a cycle is detected, an object will be appended with the
+ * Object.toString()
format.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ * @param detail output detail or not
+ */
+ protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
+ if (isRegistered(value)
+ && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
+ appendCyclicObject(buffer, fieldName, value);
+ return;
+ }
+
+ register(value);
+
+ try {
+ if (value instanceof Collection>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Collection>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Collection>) value).size());
+ }
+
+ } else if (value instanceof Map, ?>) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Map, ?>) value);
+ } else {
+ appendSummarySize(buffer, fieldName, ((Map, ?>) value).size());
+ }
+
+ } else if (value instanceof long[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (long[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (long[]) value);
+ }
+
+ } else if (value instanceof int[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (int[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (int[]) value);
+ }
+
+ } else if (value instanceof short[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (short[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (short[]) value);
+ }
+
+ } else if (value instanceof byte[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (byte[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (byte[]) value);
+ }
+
+ } else if (value instanceof char[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (char[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (char[]) value);
+ }
+
+ } else if (value instanceof double[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (double[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (double[]) value);
+ }
+
+ } else if (value instanceof float[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (float[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (float[]) value);
+ }
+
+ } else if (value instanceof boolean[]) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (boolean[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (boolean[]) value);
+ }
+
+ } else if (value.getClass().isArray()) {
+ if (detail) {
+ appendDetail(buffer, fieldName, (Object[]) value);
+ } else {
+ appendSummary(buffer, fieldName, (Object[]) value);
+ }
+
+ } else {
+ if (detail) {
+ appendDetail(buffer, fieldName, value);
+ } else {
+ appendSummary(buffer, fieldName, value);
+ }
+ }
+ } finally {
+ unregister(value);
+ }
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value that has been detected to participate in a cycle. This
+ * implementation will print the standard string value of the value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ *
+ * @since 2.2
+ */
+ protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
+ ObjectUtils.identityToString(buffer, value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing the full detail of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
a Collection
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param coll the Collection
to add to the
+ * toString
, not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection> coll) {
+ buffer.append(coll);
+ }
+
+ /**
+ * Append to the toString
a Map
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param map the Map
to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map, ?> map) {
+ buffer.append(map);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * value, printing a summary of the Object
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
+ buffer.append(summaryObjectStartText);
+ buffer.append(getShortClassName(value.getClass()));
+ buffer.append(summaryObjectEndText);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a long
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
an int
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a short
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a byte
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a char
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a double
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a float
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
+ buffer.append(value);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param value the value to add to the toString
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
+ appendFieldStart(buffer, fieldName);
+ appendDetail(buffer, fieldName, value);
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
a boolean
+ * value.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param value the value to add to the toString
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
+ buffer.append(value);
+ }
+
+ /**
+ * Append to the toString
an Object
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the detail of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ final Object item = array[i];
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ if (item == null) {
+ appendNullText(buffer, fieldName);
+
+ } else {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+/**
+ * Append to the toString
the detail of an array type.
+ *
+ * @param buffer
+ * the StringBuffer
to populate
+ * @param fieldName
+ * the field name, typically not used as already appended
+ * @param array
+ * the array to add to the toString
,
+ * not null
+ * @since 2.0
+ */
+protected void reflectionAppendArrayDetail(final java.lang.StringBuffer buffer, final java.lang.String fieldName, final java.lang.Object array) {
+ buffer.append(arrayStart);
+ final int length = java.lang.reflect.Array.getLength(array);
+ for (int i = 0; i < length; i++) {
+ final java.lang.Object item = java.lang.reflect.Array.get(array, i);
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ {
+ appendInternal(buffer, fieldName, item, arrayContentDetail);
+ }
+ }
+ buffer.append(arrayEnd);
+}
+
+ /**
+ * Append to the toString
a summary of an
+ * Object
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a long
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * long
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
an int
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of an
+ * int
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a short
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * short
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a byte
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * byte
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a char
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * char
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a double
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * double
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a float
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * float
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
a boolean
+ * array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ * @param array the array to add to the toString
+ * @param fullDetail true
for detail, false
+ * for summary info, null
for style decides
+ */
+ public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
+ appendFieldStart(buffer, fieldName);
+
+ if (array == null) {
+ appendNullText(buffer, fieldName);
+
+ } else if (isFullDetail(fullDetail)) {
+ appendDetail(buffer, fieldName, array);
+
+ } else {
+ appendSummary(buffer, fieldName, array);
+ }
+
+ appendFieldEnd(buffer, fieldName);
+ }
+
+ /**
+ * Append to the toString
the detail of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ buffer.append(arrayStart);
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(arraySeparator);
+ }
+ appendDetail(buffer, fieldName, array[i]);
+ }
+ buffer.append(arrayEnd);
+ }
+
+ /**
+ * Append to the toString
a summary of a
+ * boolean
array.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param array the array to add to the toString
,
+ * not null
+ */
+ protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
+ appendSummarySize(buffer, fieldName, array.length);
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Append to the toString
the class name.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose name to output
+ */
+ protected void appendClassName(final StringBuffer buffer, final Object object) {
+ if (useClassName && object != null) {
+ register(object);
+ if (useShortClassName) {
+ buffer.append(getShortClassName(object.getClass()));
+ } else {
+ buffer.append(object.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Append the {@link System#identityHashCode(java.lang.Object)}.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param object the Object
whose id to output
+ */
+ protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
+ if (this.isUseIdentityHashCode() && object!=null) {
+ register(object);
+ buffer.append('@');
+ buffer.append(Integer.toHexString(System.identityHashCode(object)));
+ }
+ }
+
+ /**
+ * Append to the toString
the content start.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentStart(final StringBuffer buffer) {
+ buffer.append(contentStart);
+ }
+
+ /**
+ * Append to the toString
the content end.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendContentEnd(final StringBuffer buffer) {
+ buffer.append(contentEnd);
+ }
+
+ /**
+ * Append to the toString
an indicator for null
.
+ *
+ * The default indicator is '<null>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendNullText(final StringBuffer buffer, final String fieldName) {
+ buffer.append(nullText);
+ }
+
+ /**
+ * Append to the toString
the field separator.
+ *
+ * @param buffer the StringBuffer
to populate
+ */
+ protected void appendFieldSeparator(final StringBuffer buffer) {
+ buffer.append(fieldSeparator);
+ }
+
+ /**
+ * Append to the toString
the field start.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name
+ */
+ protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
+ if (useFieldNames && fieldName != null) {
+ buffer.append(fieldName);
+ buffer.append(fieldNameValueSeparator);
+ }
+ }
+
+ /**
+ * Append to the toString
the field end.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ */
+ protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
+ appendFieldSeparator(buffer);
+ }
+
+ /**
+ * Append to the toString
a size summary.
+ *
+ * The size summary is used to summarize the contents of
+ * Collections
, Maps
and arrays.
+ *
+ * The output consists of a prefix, the passed in size
+ * and a suffix.
+ *
+ * The default format is '<size=n>'
.
+ *
+ * @param buffer the StringBuffer
to populate
+ * @param fieldName the field name, typically not used as already appended
+ * @param size the size to append
+ */
+ protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
+ buffer.append(sizeStartText);
+ buffer.append(size);
+ buffer.append(sizeEndText);
+ }
+
+ /**
+ * Is this field to be output in full detail.
+ *
+ * This method converts a detail request into a detail level.
+ * The calling code may request full detail (true
),
+ * but a subclass might ignore that and always return
+ * false
. The calling code may pass in
+ * null
indicating that it doesn't care about
+ * the detail level. In this case the default detail level is
+ * used.
+ *
+ * @param fullDetailRequest the detail level requested
+ * @return whether full detail is to be shown
+ */
+ protected boolean isFullDetail(final Boolean fullDetailRequest) {
+ if (fullDetailRequest == null) {
+ return defaultFullDetail;
+ }
+ return fullDetailRequest.booleanValue();
+ }
+
+ /**
+ * Gets the short class name for a class.
+ *
+ * The short class name is the classname excluding
+ * the package name.
+ *
+ * @param cls the Class
to get the short name of
+ * @return the short name
+ */
+ protected String getShortClassName(final Class> cls) {
+ return ClassUtils.getShortClassName(cls);
+ }
+
+ // Setters and getters for the customizable parts of the style
+ // These methods are not expected to be overridden, except to make public
+ // (They are not public so that immutable subclasses can be written)
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the class name.
+ *
+ * @return the current useClassName flag
+ */
+ protected boolean isUseClassName() {
+ return useClassName;
+ }
+
+ /**
+ * Sets whether to use the class name.
+ *
+ * @param useClassName the new useClassName flag
+ */
+ protected void setUseClassName(final boolean useClassName) {
+ this.useClassName = useClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output short or long class names.
+ *
+ * @return the current useShortClassName flag
+ * @since 2.0
+ */
+ protected boolean isUseShortClassName() {
+ return useShortClassName;
+ }
+
+ /**
+ * Sets whether to output short or long class names.
+ *
+ * @param useShortClassName the new useShortClassName flag
+ * @since 2.0
+ */
+ protected void setUseShortClassName(final boolean useShortClassName) {
+ this.useShortClassName = useShortClassName;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the identity hash code.
+ *
+ * @return the current useIdentityHashCode flag
+ */
+ protected boolean isUseIdentityHashCode() {
+ return useIdentityHashCode;
+ }
+
+ /**
+ * Sets whether to use the identity hash code.
+ *
+ * @param useIdentityHashCode the new useIdentityHashCode flag
+ */
+ protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
+ this.useIdentityHashCode = useIdentityHashCode;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use the field names passed in.
+ *
+ * @return the current useFieldNames flag
+ */
+ protected boolean isUseFieldNames() {
+ return useFieldNames;
+ }
+
+ /**
+ * Sets whether to use the field names passed in.
+ *
+ * @param useFieldNames the new useFieldNames flag
+ */
+ protected void setUseFieldNames(final boolean useFieldNames) {
+ this.useFieldNames = useFieldNames;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @return the current defaultFullDetail flag
+ */
+ protected boolean isDefaultFullDetail() {
+ return defaultFullDetail;
+ }
+
+ /**
+ * Sets whether to use full detail when the caller doesn't
+ * specify.
+ *
+ * @param defaultFullDetail the new defaultFullDetail flag
+ */
+ protected void setDefaultFullDetail(final boolean defaultFullDetail) {
+ this.defaultFullDetail = defaultFullDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether to output array content detail.
+ *
+ * @return the current array content detail setting
+ */
+ protected boolean isArrayContentDetail() {
+ return arrayContentDetail;
+ }
+
+ /**
+ * Sets whether to output array content detail.
+ *
+ * @param arrayContentDetail the new arrayContentDetail flag
+ */
+ protected void setArrayContentDetail(final boolean arrayContentDetail) {
+ this.arrayContentDetail = arrayContentDetail;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array start text.
+ *
+ * @return the current array start text
+ */
+ protected String getArrayStart() {
+ return arrayStart;
+ }
+
+ /**
+ * Sets the array start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayStart the new array start text
+ */
+ protected void setArrayStart(String arrayStart) {
+ if (arrayStart == null) {
+ arrayStart = StringUtils.EMPTY;
+ }
+ this.arrayStart = arrayStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array end text.
+ *
+ * @return the current array end text
+ */
+ protected String getArrayEnd() {
+ return arrayEnd;
+ }
+
+ /**
+ * Sets the array end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arrayEnd the new array end text
+ */
+ protected void setArrayEnd(String arrayEnd) {
+ if (arrayEnd == null) {
+ arrayEnd = StringUtils.EMPTY;
+ }
+ this.arrayEnd = arrayEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the array separator text.
+ *
+ * @return the current array separator text
+ */
+ protected String getArraySeparator() {
+ return arraySeparator;
+ }
+
+ /**
+ * Sets the array separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param arraySeparator the new array separator text
+ */
+ protected void setArraySeparator(String arraySeparator) {
+ if (arraySeparator == null) {
+ arraySeparator = StringUtils.EMPTY;
+ }
+ this.arraySeparator = arraySeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content start text.
+ *
+ * @return the current content start text
+ */
+ protected String getContentStart() {
+ return contentStart;
+ }
+
+ /**
+ * Sets the content start text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentStart the new content start text
+ */
+ protected void setContentStart(String contentStart) {
+ if (contentStart == null) {
+ contentStart = StringUtils.EMPTY;
+ }
+ this.contentStart = contentStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the content end text.
+ *
+ * @return the current content end text
+ */
+ protected String getContentEnd() {
+ return contentEnd;
+ }
+
+ /**
+ * Sets the content end text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param contentEnd the new content end text
+ */
+ protected void setContentEnd(String contentEnd) {
+ if (contentEnd == null) {
+ contentEnd = StringUtils.EMPTY;
+ }
+ this.contentEnd = contentEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field name value separator text.
+ *
+ * @return the current field name value separator text
+ */
+ protected String getFieldNameValueSeparator() {
+ return fieldNameValueSeparator;
+ }
+
+ /**
+ * Sets the field name value separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldNameValueSeparator the new field name value separator text
+ */
+ protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
+ if (fieldNameValueSeparator == null) {
+ fieldNameValueSeparator = StringUtils.EMPTY;
+ }
+ this.fieldNameValueSeparator = fieldNameValueSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the field separator text.
+ *
+ * @return the current field separator text
+ */
+ protected String getFieldSeparator() {
+ return fieldSeparator;
+ }
+
+ /**
+ * Sets the field separator text.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param fieldSeparator the new field separator text
+ */
+ protected void setFieldSeparator(String fieldSeparator) {
+ if (fieldSeparator == null) {
+ fieldSeparator = StringUtils.EMPTY;
+ }
+ this.fieldSeparator = fieldSeparator;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @return the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtStart() {
+ return fieldSeparatorAtStart;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the start
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
+ this.fieldSeparatorAtStart = fieldSeparatorAtStart;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @return fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected boolean isFieldSeparatorAtEnd() {
+ return fieldSeparatorAtEnd;
+ }
+
+ /**
+ * Sets whether the field separator should be added at the end
+ * of each buffer.
+ *
+ * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
+ * @since 2.0
+ */
+ protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
+ this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the text to output when null
found.
+ *
+ * @return the current text to output when null found
+ */
+ protected String getNullText() {
+ return nullText;
+ }
+
+ /**
+ * Sets the text to output when null
found.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param nullText the new text to output when null found
+ */
+ protected void setNullText(String nullText) {
+ if (nullText == null) {
+ nullText = StringUtils.EMPTY;
+ }
+ this.nullText = nullText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of size text
+ */
+ protected String getSizeStartText() {
+ return sizeStartText;
+ }
+
+ /**
+ * Sets the start text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeStartText the new start of size text
+ */
+ protected void setSizeStartText(String sizeStartText) {
+ if (sizeStartText == null) {
+ sizeStartText = StringUtils.EMPTY;
+ }
+ this.sizeStartText = sizeStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of size text
+ */
+ protected String getSizeEndText() {
+ return sizeEndText;
+ }
+
+ /**
+ * Sets the end text to output when a Collection
,
+ * Map
or array size is output.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param sizeEndText the new end of size text
+ */
+ protected void setSizeEndText(String sizeEndText) {
+ if (sizeEndText == null) {
+ sizeEndText = StringUtils.EMPTY;
+ }
+ this.sizeEndText = sizeEndText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * @return the current start of summary text
+ */
+ protected String getSummaryObjectStartText() {
+ return summaryObjectStartText;
+ }
+
+ /**
+ * Sets the start text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output before the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectStartText the new start of summary text
+ */
+ protected void setSummaryObjectStartText(String summaryObjectStartText) {
+ if (summaryObjectStartText == null) {
+ summaryObjectStartText = StringUtils.EMPTY;
+ }
+ this.summaryObjectStartText = summaryObjectStartText;
+ }
+
+ //---------------------------------------------------------------------
+
+ /**
+ * Gets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * @return the current end of summary text
+ */
+ protected String getSummaryObjectEndText() {
+ return summaryObjectEndText;
+ }
+
+ /**
+ * Sets the end text to output when an Object
is
+ * output in summary mode.
+ *
+ * This is output after the size value.
+ *
+ * null
is accepted, but will be converted to
+ * an empty String.
+ *
+ * @param summaryObjectEndText the new end of summary text
+ */
+ protected void setSummaryObjectEndText(String summaryObjectEndText) {
+ if (summaryObjectEndText == null) {
+ summaryObjectEndText = StringUtils.EMPTY;
+ }
+ this.summaryObjectEndText = summaryObjectEndText;
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * Default ToStringStyle
.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class DefaultToStringStyle extends ToStringStyle {
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ DefaultToStringStyle() {
+ super();
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.DEFAULT_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out
+ * the field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoFieldNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoFieldNameToStringStyle() {
+ super();
+ this.setUseFieldNames(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_FIELD_NAMES_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that prints out the short
+ * class name and no identity hashcode.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class ShortPrefixToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ ShortPrefixToStringStyle() {
+ super();
+ this.setUseShortClassName(true);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SHORT_PREFIX_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the
+ * classname, identity hashcode, content start or field name.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class SimpleToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ SimpleToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ this.setUseFieldNames(false);
+ this.setContentStart(StringUtils.EMPTY);
+ this.setContentEnd(StringUtils.EMPTY);
+ }
+
+ /**
+ * Ensure Singleton after serialization.
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.SIMPLE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that outputs on multiple lines.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class MultiLineToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ MultiLineToStringStyle() {
+ super();
+ this.setContentStart("[");
+ this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
+ this.setFieldSeparatorAtStart(true);
+ this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.MULTI_LINE_STYLE;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------
+
+ /**
+ * ToStringStyle
that does not print out the classname
+ * and identity hashcode but prints content start and field names.
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ */
+ private static final class NoClassNameToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * Use the static constant rather than instantiating.
+ */
+ NoClassNameToStringStyle() {
+ super();
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+ }
+
+ /**
+ * Ensure Singleton
after serialization.
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.NO_CLASS_NAME_STYLE;
+ }
+
+ }
+
+ // ----------------------------------------------------------------------------
+
+ /**
+ *
+ * ToStringStyle
that outputs with JSON format.
+ *
+ *
+ *
+ * This is an inner class rather than using
+ * StandardToStringStyle
to ensure its immutability.
+ *
+ *
+ * @since 3.4
+ * @see json.org
+ */
+ private static final class JsonToStringStyle extends ToStringStyle {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The summary size text start '>'
.
+ */
+ private String FIELD_NAME_PREFIX = "\"";
+
+ /**
+ *
+ * Constructor.
+ *
+ *
+ *
+ * Use the static constant rather than instantiating.
+ *
+ */
+ JsonToStringStyle() {
+ super();
+
+ this.setUseClassName(false);
+ this.setUseIdentityHashCode(false);
+
+ this.setContentStart("{");
+ this.setContentEnd("}");
+
+ this.setArrayStart("[");
+ this.setArrayEnd("]");
+
+ this.setFieldSeparator(",");
+ this.setFieldNameValueSeparator(":");
+
+ this.setNullText("null");
+
+ this.setSummaryObjectStartText("\"<");
+ this.setSummaryObjectEndText(">\"");
+
+ this.setSizeStartText("\"\"");
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ Object[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, long[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, int[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ short[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, byte[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, char[] array,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ double[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ float[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName,
+ boolean[] array, Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, array, fullDetail);
+ }
+
+ @Override
+ public void append(StringBuffer buffer, String fieldName, Object value,
+ Boolean fullDetail) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+ if (!isFullDetail(fullDetail)){
+ throw new UnsupportedOperationException(
+ "FullDetail must be true when using JsonToStringStyle");
+ }
+
+ super.append(buffer, fieldName, value, fullDetail);
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
+ appendValueAsString(buffer, String.valueOf(value));
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+
+ if (value == null) {
+ appendNullText(buffer, fieldName);
+ return;
+ }
+
+ if (value instanceof String || value instanceof Character) {
+ appendValueAsString(buffer, value.toString());
+ return;
+ }
+
+ if (value instanceof Number || value instanceof Boolean) {
+ buffer.append(value);
+ return;
+ }
+
+ final String valueAsString = value.toString();
+ if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
+ buffer.append(value);
+ return;
+ }
+
+ appendDetail(buffer, fieldName, valueAsString);
+ }
+
+ private boolean isJsonArray(String valueAsString) {
+ return valueAsString.startsWith(getArrayStart())
+ && valueAsString.startsWith(getArrayEnd());
+ }
+
+ private boolean isJsonObject(String valueAsString) {
+ return valueAsString.startsWith(getContentStart())
+ && valueAsString.endsWith(getContentEnd());
+ }
+
+ /**
+ * Appends the given String in parenthesis to the given StringBuffer.
+ *
+ * @param buffer the StringBuffer to append the value to.
+ * @param value the value to append.
+ */
+ private void appendValueAsString(StringBuffer buffer, String value) {
+ buffer.append("\"" + value + "\"");
+ }
+
+ @Override
+ protected void appendFieldStart(StringBuffer buffer, String fieldName) {
+
+ if (fieldName == null) {
+ throw new UnsupportedOperationException(
+ "Field names are mandatory when using JsonToStringStyle");
+ }
+
+ super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
+ + FIELD_NAME_PREFIX);
+ }
+
+ /**
+ *
+ * Ensure Singleton
after serialization.
+ *
+ *
+ * @return the singleton
+ */
+ private Object readResolve() {
+ return ToStringStyle.JSON_STYLE;
+ }
+
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_963/metadata.json b/Java/commons-lang-ToStringStyle_963/metadata.json
new file mode 100644
index 000000000..1c79312c5
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_963/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-ToStringStyle_963",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 940,
+ "npe_method": "reflectionAppendArrayDetail",
+ "deref_field": "item",
+ "npe_class": "ToStringStyle",
+ "repo": "commons-lang",
+ "bug_id": "ToStringStyle_963"
+ }
+}
diff --git a/Java/commons-lang-ToStringStyle_963/npe.json b/Java/commons-lang-ToStringStyle_963/npe.json
new file mode 100644
index 000000000..c6e46dcb8
--- /dev/null
+++ b/Java/commons-lang-ToStringStyle_963/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java",
+ "line": 940,
+ "npe_method": "reflectionAppendArrayDetail",
+ "deref_field": "item",
+ "npe_class": "ToStringStyle"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-TypeUtils_1080/Dockerfile b/Java/commons-lang-TypeUtils_1080/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-TypeUtils_1080/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-TypeUtils_1080/buggy.java b/Java/commons-lang-TypeUtils_1080/buggy.java
new file mode 100644
index 000000000..b1ad1af94
--- /dev/null
+++ b/Java/commons-lang-TypeUtils_1080/buggy.java
@@ -0,0 +1,1854 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.reflect;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.lang3.builder.Builder;
+
+/**
+ * Utility methods focusing on type inspection, particularly with regard to
+ * generics.
+ *
+ * @since 3.0
+ */
+public class TypeUtils {
+
+ /**
+ * {@link WildcardType} builder.
+ * @since 3.2
+ */
+ public static class WildcardTypeBuilder implements Builder {
+ /**
+ * Constructor
+ */
+ private WildcardTypeBuilder() {
+ }
+
+ private Type[] upperBounds;
+ private Type[] lowerBounds;
+
+ /**
+ * Specify upper bounds of the wildcard type to build.
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withUpperBounds(final Type... bounds) {
+ this.upperBounds = bounds;
+ return this;
+ }
+
+ /**
+ * Specify lower bounds of the wildcard type to build.
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withLowerBounds(final Type... bounds) {
+ this.lowerBounds = bounds;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public WildcardType build() {
+ return new WildcardTypeImpl(upperBounds, lowerBounds);
+ }
+ }
+
+ /**
+ * GenericArrayType implementation class.
+ * @since 3.2
+ */
+ private static final class GenericArrayTypeImpl implements GenericArrayType {
+ private final Type componentType;
+
+ /**
+ * Constructor
+ * @param componentType of this array type
+ */
+ private GenericArrayTypeImpl(final Type componentType) {
+ this.componentType = componentType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getGenericComponentType() {
+ return componentType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof GenericArrayType && TypeUtils.equals(this, (GenericArrayType) obj);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int result = 67 << 4;
+ result |= componentType.hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * ParameterizedType implementation class.
+ * @since 3.2
+ */
+ private static final class ParameterizedTypeImpl implements ParameterizedType {
+ private final Class> raw;
+ private final Type useOwner;
+ private final Type[] typeArguments;
+
+ /**
+ * Constructor
+ * @param raw type
+ * @param useOwner owner type to use, if any
+ * @param typeArguments formal type arguments
+ */
+ private ParameterizedTypeImpl(final Class> raw, final Type useOwner, final Type[] typeArguments) {
+ this.raw = raw;
+ this.useOwner = useOwner;
+ this.typeArguments = typeArguments.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getRawType() {
+ return raw;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getOwnerType() {
+ return useOwner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type[] getActualTypeArguments() {
+ return typeArguments.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof ParameterizedType && TypeUtils.equals(this, ((ParameterizedType) obj));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings( "deprecation" ) // ObjectUtils.hashCode(Object) has been deprecated in 3.2
+ @Override
+ public int hashCode() {
+ int result = 71 << 4;
+ result |= raw.hashCode();
+ result <<= 4;
+ result |= ObjectUtils.hashCode(useOwner);
+ result <<= 8;
+ result |= Arrays.hashCode(typeArguments);
+ return result;
+ }
+ }
+
+ /**
+ * WildcardType implementation class.
+ * @since 3.2
+ */
+ private static final class WildcardTypeImpl implements WildcardType {
+ private static final Type[] EMPTY_BOUNDS = new Type[0];
+
+ private final Type[] upperBounds;
+ private final Type[] lowerBounds;
+
+ /**
+ * Constructor
+ * @param upperBounds of this type
+ * @param lowerBounds of this type
+ */
+ private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) {
+ this.upperBounds = ObjectUtils.defaultIfNull(upperBounds, EMPTY_BOUNDS);
+ this.lowerBounds = ObjectUtils.defaultIfNull(lowerBounds, EMPTY_BOUNDS);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type[] getUpperBounds() {
+ return upperBounds.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type[] getLowerBounds() {
+ return lowerBounds.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof WildcardType && TypeUtils.equals(this, (WildcardType) obj);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int result = 73 << 8;
+ result |= Arrays.hashCode(upperBounds);
+ result <<= 8;
+ result |= Arrays.hashCode(lowerBounds);
+ return result;
+ }
+ }
+
+ /**
+ * A wildcard instance matching {@code ?}.
+ * @since 3.2
+ */
+ public static final WildcardType WILDCARD_ALL = wildcardType().withUpperBounds(Object.class).build();
+
+ /**
+ * {@code TypeUtils} instances should NOT be constructed in standard
+ * programming. Instead, the class should be used as
+ * {@code TypeUtils.isAssignable(cls, toClass)}.
This
+ * constructor is public to permit tools that require a JavaBean instance to
+ * operate.
+ */
+ public TypeUtils() {
+ super();
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target type
+ * following the Java generics rules. If both types are {@link Class}
+ * objects, the method returns the result of
+ * {@link ClassUtils#isAssignable(Class, Class)}.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ public static boolean isAssignable(final Type type, final Type toType) {
+ return isAssignable(type, toType, null);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target type
+ * following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @param typeVarAssigns optional map of type variable assignments
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ private static boolean isAssignable(final Type type, final Type toType,
+ final Map, Type> typeVarAssigns) {
+ if (toType == null || toType instanceof Class>) {
+ return isAssignable(type, (Class>) toType);
+ }
+
+ if (toType instanceof ParameterizedType) {
+ return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof GenericArrayType) {
+ return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof WildcardType) {
+ return isAssignable(type, (WildcardType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof TypeVariable>) {
+ return isAssignable(type, (TypeVariable>) toType, typeVarAssigns);
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + toType);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target class
+ * following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toClass the target class
+ * @return {@code true} if {@code type} is assignable to {@code toClass}.
+ */
+ private static boolean isAssignable(final Type type, final Class> toClass) {
+ if (type == null) {
+ // consistency with ClassUtils.isAssignable() behavior
+ return toClass == null || !toClass.isPrimitive();
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toClass == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toClass.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof Class>) {
+ // just comparing two classes
+ return ClassUtils.isAssignable((Class>) type, toClass);
+ }
+
+ if (type instanceof ParameterizedType) {
+ // only have to compare the raw type to the class
+ return isAssignable(getRawType((ParameterizedType) type), toClass);
+ }
+
+ // *
+ if (type instanceof TypeVariable>) {
+ // if any of the bounds are assignable to the class, then the
+ // type is assignable to the class.
+ for (final Type bound : ((TypeVariable>) type).getBounds()) {
+ if (isAssignable(bound, toClass)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // the only classes to which a generic array type can be assigned
+ // are class Object and array classes
+ if (type instanceof GenericArrayType) {
+ return toClass.equals(Object.class)
+ || toClass.isArray()
+ && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass
+ .getComponentType());
+ }
+
+ // wildcard types are not assignable to a class (though one would think
+ // "? super Object" would be assignable to Object)
+ if (type instanceof WildcardType) {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target
+ * parameterized type following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toParameterizedType the target parameterized type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toParameterizedType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toParameterizedType.equals(type)) {
+ return true;
+ }
+
+ // get the target type's raw type
+ final Class> toClass = getRawType(toParameterizedType);
+ // get the subject type's type arguments including owner type arguments
+ // and supertype arguments up to and including the target class.
+ final Map, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
+
+ // null means the two types are not compatible
+ if (fromTypeVarAssigns == null) {
+ return false;
+ }
+
+ // compatible types, but there's no type arguments. this is equivalent
+ // to comparing Map< ?, ? > to Map, and raw types are always assignable
+ // to parameterized types.
+ if (fromTypeVarAssigns.isEmpty()) {
+ return true;
+ }
+
+ // get the target type's type arguments including owner type arguments
+ final Map, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType,
+ toClass, typeVarAssigns);
+
+ // now to check each type argument
+ for (final TypeVariable> var : toTypeVarAssigns.keySet()) {
+ final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
+ final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
+
+ if (toTypeArg == null && fromTypeArg instanceof Class) {
+ continue;
+ }
+
+ // parameters must either be absent from the subject type, within
+ // the bounds of the wildcard type, or be an exact match to the
+ // parameters of the target type.
+ if (fromTypeArg != null
+ && !toTypeArg.equals(fromTypeArg)
+ && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg,
+ typeVarAssigns))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Look up {@code var} in {@code typeVarAssigns} transitively ,
+ * i.e. keep looking until the value found is not a type variable.
+ * @param var the type variable to look up
+ * @param typeVarAssigns the map used for the look up
+ * @return Type or {@code null} if some variable was not in the map
+ * @since 3.2
+ */
+ private static Type unrollVariableAssignments(TypeVariable> var, final Map, Type> typeVarAssigns) {
+ Type result;
+ do {
+ result = typeVarAssigns.get(var);
+ if (result instanceof TypeVariable> && !result.equals(var)) {
+ var = (TypeVariable>) result;
+ continue;
+ }
+ break;
+ } while (true);
+ return result;
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target
+ * generic array type following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toGenericArrayType the target generic array type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toGenericArrayType}.
+ */
+ private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toGenericArrayType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toGenericArrayType.equals(type)) {
+ return true;
+ }
+
+ final Type toComponentType = toGenericArrayType.getGenericComponentType();
+
+ if (type instanceof Class>) {
+ final Class> cls = (Class>) type;
+
+ // compare the component types
+ return cls.isArray()
+ && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
+ }
+
+ if (type instanceof GenericArrayType) {
+ // compare the component types
+ return isAssignable(((GenericArrayType) type).getGenericComponentType(),
+ toComponentType, typeVarAssigns);
+ }
+
+ if (type instanceof WildcardType) {
+ // so long as one of the upper bounds is assignable, it's good
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (type instanceof TypeVariable>) {
+ // probably should remove the following logic and just return false.
+ // type variables cannot specify arrays as bounds.
+ for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (type instanceof ParameterizedType) {
+ // the raw type of a parameterized type is never an array or
+ // generic array, otherwise the declaration would look like this:
+ // Collection[]< ? extends String > collection;
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target
+ * wildcard type following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toWildcardType the target wildcard type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toWildcardType}.
+ */
+ private static boolean isAssignable(final Type type, final WildcardType toWildcardType,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toWildcardType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toWildcardType.equals(type)) {
+ return true;
+ }
+
+ final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
+ final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
+
+ if (type instanceof WildcardType) {
+ final WildcardType wildcardType = (WildcardType) type;
+ final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
+ final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
+
+ for (Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
+
+ // each upper bound of the subject type has to be assignable to
+ // each
+ // upper bound of the target type
+ for (final Type bound : upperBounds) {
+ if (!isAssignable(bound, toBound, typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+
+ for (Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
+
+ // each lower bound of the target type has to be assignable to
+ // each
+ // lower bound of the subject type
+ for (final Type bound : lowerBounds) {
+ if (!isAssignable(toBound, bound, typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ for (final Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns),
+ typeVarAssigns)) {
+ return false;
+ }
+ }
+
+ for (final Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type,
+ typeVarAssigns)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target type
+ * variable following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toTypeVariable the target type variable
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toTypeVariable}.
+ */
+ private static boolean isAssignable(final Type type, final TypeVariable> toTypeVariable,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toTypeVariable == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toTypeVariable.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof TypeVariable>) {
+ // a type variable is assignable to another type variable, if
+ // and only if the former is the latter, extends the latter, or
+ // is otherwise a descendant of the latter.
+ final Type[] bounds = getImplicitBounds((TypeVariable>) type);
+
+ for (final Type bound : bounds) {
+ if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
+ return true;
+ }
+ }
+ }
+
+ if (type instanceof Class> || type instanceof ParameterizedType
+ || type instanceof GenericArrayType || type instanceof WildcardType) {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Find the mapping for {@code type} in {@code typeVarAssigns}.
+ *
+ * @param type the type to be replaced
+ * @param typeVarAssigns the map with type variables
+ * @return the replaced type
+ * @throws IllegalArgumentException if the type cannot be substituted
+ */
+ private static Type substituteTypeVariables(final Type type, final Map, Type> typeVarAssigns) {
+ if (type instanceof TypeVariable> && typeVarAssigns != null) {
+ final Type replacementType = typeVarAssigns.get(type);
+
+ if (replacementType == null) {
+ throw new IllegalArgumentException("missing assignment type for type variable "
+ + type);
+ }
+ return replacementType;
+ }
+ return type;
+ }
+
+ /**
+ * Retrieves all the type arguments for this parameterized type
+ * including owner hierarchy arguments such as
+ * {@code Outer.Inner.DeepInner} .
+ * The arguments are returned in a
+ * {@link Map} specifying the argument type for each {@link TypeVariable}.
+ *
+ *
+ * @param type specifies the subject parameterized type from which to
+ * harvest the parameters.
+ * @return a {@code Map} of the type arguments to their respective type
+ * variables.
+ */
+ public static Map, Type> getTypeArguments(final ParameterizedType type) {
+ return getTypeArguments(type, getRawType(type), null);
+ }
+
+ /**
+ * Gets the type arguments of a class/interface based on a subtype. For
+ * instance, this method will determine that both of the parameters for the
+ * interface {@link Map} are {@link Object} for the subtype
+ * {@link java.util.Properties Properties} even though the subtype does not
+ * directly implement the {@code Map} interface.
+ * This method returns {@code null} if {@code type} is not assignable to
+ * {@code toClass}. It returns an empty map if none of the classes or
+ * interfaces in its inheritance hierarchy specify any type arguments.
+ * A side effect of this method is that it also retrieves the type
+ * arguments for the classes and interfaces that are part of the hierarchy
+ * between {@code type} and {@code toClass}. So with the above
+ * example, this method will also determine that the type arguments for
+ * {@link java.util.Hashtable Hashtable} are also both {@code Object}.
+ * In cases where the interface specified by {@code toClass} is
+ * (indirectly) implemented more than once (e.g. where {@code toClass}
+ * specifies the interface {@link java.lang.Iterable Iterable} and
+ * {@code type} specifies a parameterized type that implements both
+ * {@link java.util.Set Set} and {@link java.util.Collection Collection}),
+ * this method will look at the inheritance hierarchy of only one of the
+ * implementations/subclasses; the first interface encountered that isn't a
+ * subinterface to one of the others in the {@code type} to
+ * {@code toClass} hierarchy.
+ *
+ * @param type the type from which to determine the type parameters of
+ * {@code toClass}
+ * @param toClass the class whose type parameters are to be determined based
+ * on the subtype {@code type}
+ * @return a {@code Map} of the type assignments for the type variables in
+ * each type in the inheritance hierarchy from {@code type} to
+ * {@code toClass} inclusive.
+ */
+ public static Map, Type> getTypeArguments(final Type type, final Class> toClass) {
+ return getTypeArguments(type, toClass, null);
+ }
+
+ /**
+ * Return a map of the type arguments of @{code type} in the context of {@code toClass}.
+ *
+ * @param type the type in question
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(final Type type, final Class> toClass,
+ final Map, Type> subtypeVarAssigns) {
+ if (type instanceof Class>) {
+ return getTypeArguments((Class>) type, toClass, subtypeVarAssigns);
+ }
+
+ if (type instanceof ParameterizedType) {
+ return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns);
+ }
+
+ if (type instanceof GenericArrayType) {
+ return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass
+ .isArray() ? toClass.getComponentType() : toClass, subtypeVarAssigns);
+ }
+
+ // since wildcard types are not assignable to classes, should this just
+ // return null?
+ if (type instanceof WildcardType) {
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ // find the first bound that is assignable to the target class
+ if (isAssignable(bound, toClass)) {
+ return getTypeArguments(bound, toClass, subtypeVarAssigns);
+ }
+ }
+
+ return null;
+ }
+
+ if (type instanceof TypeVariable>) {
+ for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
+ // find the first bound that is assignable to the target class
+ if (isAssignable(bound, toClass)) {
+ return getTypeArguments(bound, toClass, subtypeVarAssigns);
+ }
+ }
+
+ return null;
+ }
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Return a map of the type arguments of a parameterized type in the context of {@code toClass}.
+ *
+ * @param parameterizedType the parameterized type
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(
+ final ParameterizedType parameterizedType, final Class> toClass,
+ final Map, Type> subtypeVarAssigns) {
+ final Class> cls = getRawType(parameterizedType);
+
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ final Type ownerType = parameterizedType.getOwnerType();
+ Map, Type> typeVarAssigns;
+
+ if (ownerType instanceof ParameterizedType) {
+ // get the owner type arguments first
+ final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
+ typeVarAssigns = getTypeArguments(parameterizedOwnerType,
+ getRawType(parameterizedOwnerType), subtypeVarAssigns);
+ } else {
+ // no owner, prep the type variable assignments map
+ typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>()
+ : new HashMap, Type>(subtypeVarAssigns);
+ }
+
+ // get the subject parameterized type's arguments
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+ // and get the corresponding type variables from the raw class
+ final TypeVariable>[] typeParams = cls.getTypeParameters();
+
+ // map the arguments to their respective type variables
+ for (int i = 0; i < typeParams.length; i++) {
+ final Type typeArg = typeArgs[i];
+ typeVarAssigns.put(typeParams[i], typeVarAssigns.containsKey(typeArg) ? typeVarAssigns
+ .get(typeArg) : typeArg);
+ }
+
+ if (toClass.equals(cls)) {
+ // target class has been reached. Done.
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
+ }
+
+ /**
+ * Return a map of the type arguments of a class in the context of @{code toClass}.
+ *
+ * @param cls the class in question
+ * @param toClass the context class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(Class> cls, final Class> toClass,
+ final Map, Type> subtypeVarAssigns) {
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ // can't work with primitives
+ if (cls.isPrimitive()) {
+ // both classes are primitives?
+ if (toClass.isPrimitive()) {
+ // dealing with widening here. No type arguments to be
+ // harvested with these two types.
+ return new HashMap, Type>();
+ }
+
+ // work with wrapper the wrapper class instead of the primitive
+ cls = ClassUtils.primitiveToWrapper(cls);
+ }
+
+ // create a copy of the incoming map, or an empty one if it's null
+ final HashMap, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>()
+ : new HashMap, Type>(subtypeVarAssigns);
+
+ // has target class been reached?
+ if (toClass.equals(cls)) {
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
+ }
+
+ /**
+ * Tries to determine the type arguments of a class/interface based on a
+ * super parameterized type's type arguments. This method is the inverse of
+ * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
+ * type arguments based on a subtype. It is far more limited in determining
+ * the type arguments for the subject class's type variables in that it can
+ * only determine those parameters that map from the subject {@link Class}
+ * object to the supertype.
Example: {@link java.util.TreeSet
+ * TreeSet} sets its parameter as the parameter for
+ * {@link java.util.NavigableSet NavigableSet}, which in turn sets the
+ * parameter of {@link java.util.SortedSet}, which in turn sets the
+ * parameter of {@link Set}, which in turn sets the parameter of
+ * {@link java.util.Collection}, which in turn sets the parameter of
+ * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps
+ * (indirectly) to {@code Iterable}'s parameter, it will be able to
+ * determine that based on the super type {@code Iterable extends
+ * Map>>}, the parameter of
+ * {@code TreeSet} is {@code ? extends Map>}.
+ *
+ * @param cls the class whose type parameters are to be determined, not {@code null}
+ * @param superType the super type from which {@code cls}'s type
+ * arguments are to be determined, not {@code null}
+ * @return a {@code Map} of the type assignments that could be determined
+ * for the type variables in each type in the inheritance hierarchy from
+ * {@code type} to {@code toClass} inclusive.
+ */
+ public static Map, Type> determineTypeArguments(final Class> cls,
+ final ParameterizedType superType) {
+ Validate.notNull(cls, "cls is null");
+ Validate.notNull(superType, "superType is null");
+
+ final Class> superClass = getRawType(superType);
+
+ // compatibility check
+ if (!isAssignable(cls, superClass)) {
+ return null;
+ }
+
+ if (cls.equals(superClass)) {
+ return getTypeArguments(superType, superClass, null);
+ }
+
+ // get the next class in the inheritance hierarchy
+ final Type midType = getClosestParentType(cls, superClass);
+
+ // can only be a class or a parameterized type
+ if (midType instanceof Class>) {
+ return determineTypeArguments((Class>) midType, superType);
+ }
+
+ final ParameterizedType midParameterizedType = (ParameterizedType) midType;
+ final Class> midClass = getRawType(midParameterizedType);
+ // get the type variables of the mid class that map to the type
+ // arguments of the super class
+ final Map, Type> typeVarAssigns = determineTypeArguments(midClass, superType);
+ // map the arguments of the mid type to the class type variables
+ mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
+
+ return typeVarAssigns;
+ }
+
+ /**
+ * Performs a mapping of type variables.
+ *
+ * @param the generic type of the class in question
+ * @param cls the class in question
+ * @param parameterizedType the parameterized type
+ * @param typeVarAssigns the map to be filled
+ */
+ private static void mapTypeVariablesToArguments(final Class cls,
+ final ParameterizedType parameterizedType, final Map, Type> typeVarAssigns) {
+ // capture the type variables from the owner type that have assignments
+ final Type ownerType = parameterizedType.getOwnerType();
+
+ if (ownerType instanceof ParameterizedType) {
+ // recursion to make sure the owner's owner type gets processed
+ mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
+ }
+
+ // parameterizedType is a generic interface/class (or it's in the owner
+ // hierarchy of said interface/class) implemented/extended by the class
+ // cls. Find out which type variables of cls are type arguments of
+ // parameterizedType:
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+
+ // of the cls's type variables that are arguments of parameterizedType,
+ // find out which ones can be determined from the super type's arguments
+ final TypeVariable>[] typeVars = getRawType(parameterizedType).getTypeParameters();
+
+ // use List view of type parameters of cls so the contains() method can be used:
+ final List>> typeVarList = Arrays.asList(cls
+ .getTypeParameters());
+
+ for (int i = 0; i < typeArgs.length; i++) {
+ final TypeVariable> typeVar = typeVars[i];
+ final Type typeArg = typeArgs[i];
+
+ // argument of parameterizedType is a type variable of cls
+ if (typeVarList.contains(typeArg)
+ // type variable of parameterizedType has an assignment in
+ // the super type.
+ && typeVarAssigns.containsKey(typeVar)) {
+ // map the assignment to the cls's type variable
+ typeVarAssigns.put((TypeVariable>) typeArg, typeVarAssigns.get(typeVar));
+ }
+ }
+ }
+
+ /**
+ * Get the closest parent type to the
+ * super class specified by {@code superClass}.
+ *
+ * @param cls the class in question
+ * @param superClass the super class
+ * @return the closes parent type
+ */
+/**
+ * Get the closest parent type to the
+ * super class specified by {@code superClass}.
+ *
+ * @param cls
+ * the class in question
+ * @param superClass
+ * the super class
+ * @return the closes parent type
+ */
+private static java.lang.reflect.Type getClosestParentType(final java.lang.Class> cls, final java.lang.Class> superClass) {
+ // only look at the interfaces if the super class is also an interface
+ if (superClass.isInterface()) {
+ // get the generic interfaces of the subject class
+ final java.lang.reflect.Type[] interfaceTypes = cls.getGenericInterfaces();
+ // will hold the best generic interface match found
+ java.lang.reflect.Type genericInterface = null;
+ // find the interface closest to the super class
+ for (final java.lang.reflect.Type midType : interfaceTypes) {
+ java.lang.Class> midClass = null;
+ if (midType instanceof java.lang.reflect.ParameterizedType) {
+ midClass = org.apache.commons.lang3.reflect.TypeUtils.getRawType(((java.lang.reflect.ParameterizedType) (midType)));
+ } else if (midType instanceof java.lang.Class>) {
+ midClass = ((java.lang.Class>) (midType));
+ } else {
+ throw new java.lang.IllegalStateException(("Unexpected generic" + " interface type found: ") + midType);
+ }
+ // check if this interface is further up the inheritance chain
+ // than the previously found match
+ if (org.apache.commons.lang3.reflect.TypeUtils.isAssignable(midClass, superClass) && org.apache.commons.lang3.reflect.TypeUtils.isAssignable(genericInterface, ((java.lang.reflect.Type) (midClass)))) {
+ genericInterface = midType;
+ }
+ }
+ {
+ return /* NPEX_NULL_EXP */
+ genericInterface;
+ }
+ }
+ // none of the interfaces were descendants of the target class, so the
+ // super class has to be one, instead
+ return cls.getGenericSuperclass();
+}
+
+ /**
+ * Checks if the given value can be assigned to the target type
+ * following the Java generics rules.
+ *
+ * @param value the value to be checked
+ * @param type the target type
+ * @return {@code true} if {@code value} is an instance of {@code type}.
+ */
+ public static boolean isInstance(final Object value, final Type type) {
+ if (type == null) {
+ return false;
+ }
+
+ return value == null ? !(type instanceof Class>) || !((Class>) type).isPrimitive()
+ : isAssignable(value.getClass(), type, null);
+ }
+
+ /**
+ * This method strips out the redundant upper bound types in type
+ * variable types and wildcard types (or it would with wildcard types if
+ * multiple upper bounds were allowed).
Example, with the variable
+ * type declaration:
+ *
+ *
<K extends java.util.Collection<String> &
+ * java.util.List<String>>
+ *
+ *
+ * since {@code List} is a subinterface of {@code Collection},
+ * this method will return the bounds as if the declaration had been:
+ *
+ *
+ * <K extends java.util.List<String>>
+ *
+ * @param bounds an array of types representing the upper bounds of either
+ * {@link WildcardType} or {@link TypeVariable}, not {@code null}.
+ * @return an array containing the values from {@code bounds} minus the
+ * redundant types.
+ */
+ public static Type[] normalizeUpperBounds(final Type[] bounds) {
+ Validate.notNull(bounds, "null value specified for bounds array");
+ // don't bother if there's only one (or none) type
+ if (bounds.length < 2) {
+ return bounds;
+ }
+
+ final Set types = new HashSet(bounds.length);
+
+ for (final Type type1 : bounds) {
+ boolean subtypeFound = false;
+
+ for (final Type type2 : bounds) {
+ if (type1 != type2 && isAssignable(type2, type1, null)) {
+ subtypeFound = true;
+ break;
+ }
+ }
+
+ if (!subtypeFound) {
+ types.add(type1);
+ }
+ }
+
+ return types.toArray(new Type[types.size()]);
+ }
+
+ /**
+ * Returns an array containing the sole type of {@link Object} if
+ * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
+ * returns the result of {@link TypeVariable#getBounds()} passed into
+ * {@link #normalizeUpperBounds}.
+ *
+ * @param typeVariable the subject type variable, not {@code null}
+ * @return a non-empty array containing the bounds of the type variable.
+ */
+ public static Type[] getImplicitBounds(final TypeVariable> typeVariable) {
+ Validate.notNull(typeVariable, "typeVariable is null");
+ final Type[] bounds = typeVariable.getBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ * Returns an array containing the sole value of {@link Object} if
+ * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getUpperBounds()}
+ * passed into {@link #normalizeUpperBounds}.
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the upper bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) {
+ Validate.notNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getUpperBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ * Returns an array containing a single value of {@code null} if
+ * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getLowerBounds()}.
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the lower bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) {
+ Validate.notNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getLowerBounds();
+
+ return bounds.length == 0 ? new Type[] { null } : bounds;
+ }
+
+ /**
+ * Determines whether or not specified types satisfy the bounds of their
+ * mapped type variables. When a type parameter extends another (such as
+ * {@code }), uses another as a type parameter (such as
+ * {@code >}), or otherwise depends on
+ * another type variable to be specified, the dependencies must be included
+ * in {@code typeVarAssigns}.
+ *
+ * @param typeVarAssigns specifies the potential types to be assigned to the
+ * type variables, not {@code null}.
+ * @return whether or not the types can be assigned to their respective type
+ * variables.
+ */
+ public static boolean typesSatisfyVariables(final Map, Type> typeVarAssigns) {
+ Validate.notNull(typeVarAssigns, "typeVarAssigns is null");
+ // all types must be assignable to all the bounds of the their mapped
+ // type variable.
+ for (final Map.Entry, Type> entry : typeVarAssigns.entrySet()) {
+ final TypeVariable> typeVar = entry.getKey();
+ final Type type = entry.getValue();
+
+ for (final Type bound : getImplicitBounds(typeVar)) {
+ if (!isAssignable(type, substituteTypeVariables(bound, typeVarAssigns),
+ typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Transforms the passed in type to a {@link Class} object. Type-checking method of convenience.
+ *
+ * @param parameterizedType the type to be converted
+ * @return the corresponding {@code Class} object
+ * @throws IllegalStateException if the conversion fails
+ */
+ private static Class> getRawType(final ParameterizedType parameterizedType) {
+ final Type rawType = parameterizedType.getRawType();
+
+ // check if raw type is a Class object
+ // not currently necessary, but since the return type is Type instead of
+ // Class, there's enough reason to believe that future versions of Java
+ // may return other Type implementations. And type-safety checking is
+ // rarely a bad idea.
+ if (!(rawType instanceof Class>)) {
+ throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType);
+ }
+
+ return (Class>) rawType;
+ }
+
+ /**
+ * Get the raw type of a Java type, given its context. Primarily for use
+ * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do
+ * not know the runtime type of {@code type}: if you know you have a
+ * {@link Class} instance, it is already raw; if you know you have a
+ * {@link ParameterizedType}, its raw type is only a method call away.
+ *
+ * @param type to resolve
+ * @param assigningType type to be resolved against
+ * @return the resolved {@link Class} object or {@code null} if
+ * the type could not be resolved
+ */
+ public static Class> getRawType(final Type type, final Type assigningType) {
+ if (type instanceof Class>) {
+ // it is raw, no problem
+ return (Class>) type;
+ }
+
+ if (type instanceof ParameterizedType) {
+ // simple enough to get the raw type of a ParameterizedType
+ return getRawType((ParameterizedType) type);
+ }
+
+ if (type instanceof TypeVariable>) {
+ if (assigningType == null) {
+ return null;
+ }
+
+ // get the entity declaring this type variable
+ final Object genericDeclaration = ((TypeVariable>) type).getGenericDeclaration();
+
+ // can't get the raw type of a method- or constructor-declared type
+ // variable
+ if (!(genericDeclaration instanceof Class>)) {
+ return null;
+ }
+
+ // get the type arguments for the declaring class/interface based
+ // on the enclosing type
+ final Map, Type> typeVarAssigns = getTypeArguments(assigningType,
+ (Class>) genericDeclaration);
+
+ // enclosingType has to be a subclass (or subinterface) of the
+ // declaring type
+ if (typeVarAssigns == null) {
+ return null;
+ }
+
+ // get the argument assigned to this type variable
+ final Type typeArgument = typeVarAssigns.get(type);
+
+ if (typeArgument == null) {
+ return null;
+ }
+
+ // get the argument for this type variable
+ return getRawType(typeArgument, assigningType);
+ }
+
+ if (type instanceof GenericArrayType) {
+ // get raw component type
+ final Class> rawComponentType = getRawType(((GenericArrayType) type)
+ .getGenericComponentType(), assigningType);
+
+ // create array type from raw component type and return its class
+ return Array.newInstance(rawComponentType, 0).getClass();
+ }
+
+ // (hand-waving) this is not the method you're looking for
+ if (type instanceof WildcardType) {
+ return null;
+ }
+
+ throw new IllegalArgumentException("unknown type: " + type);
+ }
+
+ /**
+ * Learn whether the specified type denotes an array type.
+ * @param type the type to be checked
+ * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}.
+ */
+ public static boolean isArrayType(final Type type) {
+ return type instanceof GenericArrayType || type instanceof Class> && ((Class>) type).isArray();
+ }
+
+ /**
+ * Get the array component type of {@code type}.
+ * @param type the type to be checked
+ * @return component type or null if type is not an array type
+ */
+ public static Type getArrayComponentType(final Type type) {
+ if (type instanceof Class>) {
+ final Class> clazz = (Class>) type;
+ return clazz.isArray() ? clazz.getComponentType() : null;
+ }
+ if (type instanceof GenericArrayType) {
+ return ((GenericArrayType) type).getGenericComponentType();
+ }
+ return null;
+ }
+
+ /**
+ * Get a type representing {@code type} with variable assignments "unrolled."
+ *
+ * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}
+ * @param type the type to unroll variable assignments for
+ * @return Type
+ * @since 3.2
+ */
+ public static Type unrollVariables(Map, Type> typeArguments, final Type type) {
+ if (typeArguments == null) {
+ typeArguments = Collections., Type> emptyMap();
+ }
+ if (containsTypeVariables(type)) {
+ if (type instanceof TypeVariable>) {
+ return unrollVariables(typeArguments, typeArguments.get(type));
+ }
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType p = (ParameterizedType) type;
+ final Map, Type> parameterizedTypeArguments;
+ if (p.getOwnerType() == null) {
+ parameterizedTypeArguments = typeArguments;
+ } else {
+ parameterizedTypeArguments = new HashMap, Type>(typeArguments);
+ parameterizedTypeArguments.putAll(TypeUtils.getTypeArguments(p));
+ }
+ final Type[] args = p.getActualTypeArguments();
+ for (int i = 0; i < args.length; i++) {
+ final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]);
+ if (unrolled != null) {
+ args[i] = unrolled;
+ }
+ }
+ return parameterizeWithOwner(p.getOwnerType(), (Class>) p.getRawType(), args);
+ }
+ if (type instanceof WildcardType) {
+ final WildcardType wild = (WildcardType) type;
+ return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds()))
+ .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build();
+ }
+ }
+ return type;
+ }
+
+ /**
+ * Local helper method to unroll variables in a type bounds array.
+ *
+ * @param typeArguments assignments {@link Map}
+ * @param bounds in which to expand variables
+ * @return {@code bounds} with any variables reassigned
+ * @since 3.2
+ */
+ private static Type[] unrollBounds(final Map, Type> typeArguments, final Type[] bounds) {
+ Type[] result = bounds;
+ int i = 0;
+ for (; i < result.length; i++) {
+ final Type unrolled = unrollVariables(typeArguments, result[i]);
+ if (unrolled == null) {
+ result = ArrayUtils.remove(result, i--);
+ } else {
+ result[i] = unrolled;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Learn, recursively, whether any of the type parameters associated with {@code type} are bound to variables.
+ *
+ * @param type the type to check for type variables
+ * @return boolean
+ * @since 3.2
+ */
+ public static boolean containsTypeVariables(final Type type) {
+ if (type instanceof TypeVariable>) {
+ return true;
+ }
+ if (type instanceof Class>) {
+ return ((Class>) type).getTypeParameters().length > 0;
+ }
+ if (type instanceof ParameterizedType) {
+ for (final Type arg : ((ParameterizedType) type).getActualTypeArguments()) {
+ if (containsTypeVariables(arg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (type instanceof WildcardType) {
+ final WildcardType wild = (WildcardType) type;
+ return containsTypeVariables(TypeUtils.getImplicitLowerBounds(wild)[0])
+ || containsTypeVariables(TypeUtils.getImplicitUpperBounds(wild)[0]);
+ }
+ return false;
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArguments the types used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterize(final Class> raw, final Type... typeArguments) {
+ return parameterizeWithOwner(null, raw, typeArguments);
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArgMappings the mapping used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterize(final Class> raw,
+ final Map, Type> typeArgMappings) {
+ Validate.notNull(raw, "raw class is null");
+ Validate.notNull(typeArgMappings, "typeArgMappings is null");
+ return parameterizeWithOwner(null, raw, extractTypeArgumentsFrom(typeArgMappings, raw.getTypeParameters()));
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param owner the owning type
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArguments the types used for parameterization
+ *
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class> raw,
+ final Type... typeArguments) {
+ Validate.notNull(raw, "raw class is null");
+ final Type useOwner;
+ if (raw.getEnclosingClass() == null) {
+ Validate.isTrue(owner == null, "no owner allowed for top-level %s", raw);
+ useOwner = null;
+ } else if (owner == null) {
+ useOwner = raw.getEnclosingClass();
+ } else {
+ Validate.isTrue(TypeUtils.isAssignable(owner, raw.getEnclosingClass()),
+ "%s is invalid owner type for parameterized %s", owner, raw);
+ useOwner = owner;
+ }
+ Validate.noNullElements(typeArguments, "null type argument at index %s");
+ Validate.isTrue(raw.getTypeParameters().length == typeArguments.length,
+ "invalid number of type parameters specified: expected %d, got %d", raw.getTypeParameters().length,
+ typeArguments.length);
+
+ return new ParameterizedTypeImpl(raw, useOwner, typeArguments);
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param owner the owning type
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArgMappings the mapping used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class> raw,
+ final Map, Type> typeArgMappings) {
+ Validate.notNull(raw, "raw class is null");
+ Validate.notNull(typeArgMappings, "typeArgMappings is null");
+ return parameterizeWithOwner(owner, raw, extractTypeArgumentsFrom(typeArgMappings, raw.getTypeParameters()));
+ }
+
+ /**
+ * Helper method to establish the formal parameters for a parameterized type.
+ * @param mappings map containing the assignements
+ * @param variables expected map keys
+ * @return array of map values corresponding to specified keys
+ */
+ private static Type[] extractTypeArgumentsFrom(final Map, Type> mappings, final TypeVariable>[] variables) {
+ final Type[] result = new Type[variables.length];
+ int index = 0;
+ for (final TypeVariable> var : variables) {
+ Validate.isTrue(mappings.containsKey(var), "missing argument mapping for %s", toString(var));
+ result[index++] = mappings.get(var);
+ }
+ return result;
+ }
+
+ /**
+ * Get a {@link WildcardTypeBuilder}.
+ * @return {@link WildcardTypeBuilder}
+ * @since 3.2
+ */
+ public static WildcardTypeBuilder wildcardType() {
+ return new WildcardTypeBuilder();
+ }
+
+ /**
+ * Create a generic array type instance.
+ *
+ * @param componentType the type of the elements of the array. For example the component type of {@code boolean[]}
+ * is {@code boolean}
+ * @return {@link GenericArrayType}
+ * @since 3.2
+ */
+ public static GenericArrayType genericArrayType(final Type componentType) {
+ return new GenericArrayTypeImpl(Validate.notNull(componentType, "componentType is null"));
+ }
+
+ /**
+ * Check equality of types.
+ *
+ * @param t1 the first type
+ * @param t2 the second type
+ * @return boolean
+ * @since 3.2
+ */
+ @SuppressWarnings( "deprecation" ) // ObjectUtils.equals(Object, Object) has been deprecated in 3.2
+ public static boolean equals(final Type t1, final Type t2) {
+ if (ObjectUtils.equals(t1, t2)) {
+ return true;
+ }
+ if (t1 instanceof ParameterizedType) {
+ return equals((ParameterizedType) t1, t2);
+ }
+ if (t1 instanceof GenericArrayType) {
+ return equals((GenericArrayType) t1, t2);
+ }
+ if (t1 instanceof WildcardType) {
+ return equals((WildcardType) t1, t2);
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether {@code t} equals {@code p}.
+ * @param p LHS
+ * @param t RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final ParameterizedType p, final Type t) {
+ if (t instanceof ParameterizedType) {
+ final ParameterizedType other = (ParameterizedType) t;
+ if (equals(p.getRawType(), other.getRawType()) && equals(p.getOwnerType(), other.getOwnerType())) {
+ return equals(p.getActualTypeArguments(), other.getActualTypeArguments());
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether {@code t} equals {@code a}.
+ * @param a LHS
+ * @param t RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final GenericArrayType a, final Type t) {
+ return t instanceof GenericArrayType
+ && equals(a.getGenericComponentType(), ((GenericArrayType) t).getGenericComponentType());
+ }
+
+ /**
+ * Learn whether {@code t} equals {@code w}.
+ * @param w LHS
+ * @param t RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final WildcardType w, final Type t) {
+ if (t instanceof WildcardType) {
+ final WildcardType other = (WildcardType) t;
+ return equals(getImplicitLowerBounds(w), getImplicitLowerBounds(other))
+ && equals(getImplicitUpperBounds(w), getImplicitUpperBounds(other));
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether {@code t1} equals {@code t2}.
+ * @param t1 LHS
+ * @param t2 RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final Type[] t1, final Type[] t2) {
+ if (t1.length == t2.length) {
+ for (int i = 0; i < t1.length; i++) {
+ if (!equals(t1[i], t2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Present a given type as a Java-esque String.
+ *
+ * @param type the type to create a String representation for, not {@code null}
+ * @return String
+ * @since 3.2
+ */
+ public static String toString(final Type type) {
+ Validate.notNull(type);
+ if (type instanceof Class>) {
+ return classToString((Class>) type);
+ }
+ if (type instanceof ParameterizedType) {
+ return parameterizedTypeToString((ParameterizedType) type);
+ }
+ if (type instanceof WildcardType) {
+ return wildcardTypeToString((WildcardType) type);
+ }
+ if (type instanceof TypeVariable>) {
+ return typeVariableToString((TypeVariable>) type);
+ }
+ if (type instanceof GenericArrayType) {
+ return genericArrayTypeToString((GenericArrayType) type);
+ }
+ throw new IllegalArgumentException(ObjectUtils.identityToString(type));
+ }
+
+ /**
+ * Format a {@link TypeVariable} including its {@link GenericDeclaration}.
+ *
+ * @param var the type variable to create a String representation for, not {@code null}
+ * @return String
+ * @since 3.2
+ */
+ public static String toLongString(final TypeVariable> var) {
+ Validate.notNull(var, "var is null");
+ final StringBuilder buf = new StringBuilder();
+ final GenericDeclaration d = ((TypeVariable>) var).getGenericDeclaration();
+ if (d instanceof Class>) {
+ Class> c = (Class>) d;
+ while (true) {
+ if (c.getEnclosingClass() == null) {
+ buf.insert(0, c.getName());
+ break;
+ }
+ buf.insert(0, c.getSimpleName()).insert(0, '.');
+ c = c.getEnclosingClass();
+ }
+ } else if (d instanceof Type) {// not possible as of now
+ buf.append(toString((Type) d));
+ } else {
+ buf.append(d);
+ }
+ return buf.append(':').append(typeVariableToString(var)).toString();
+ }
+
+ /**
+ * Wrap the specified {@link Type} in a {@link Typed} wrapper.
+ *
+ * @param inferred generic type
+ * @param type to wrap
+ * @return Typed<T>
+ * @since 3.2
+ */
+ public static Typed wrap(final Type type) {
+ return new Typed() {
+ @Override
+ public Type getType() {
+ return type;
+ }
+ };
+ }
+
+ /**
+ * Wrap the specified {@link Class} in a {@link Typed} wrapper.
+ *
+ * @param generic type
+ * @param type to wrap
+ * @return Typed<T>
+ * @since 3.2
+ */
+ public static Typed wrap(final Class type) {
+ return TypeUtils. wrap((Type) type);
+ }
+
+ /**
+ * Format a {@link Class} as a {@link String}.
+ * @param c {@code Class} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String classToString(final Class> c) {
+ final StringBuilder buf = new StringBuilder();
+
+ if (c.getEnclosingClass() != null) {
+ buf.append(classToString(c.getEnclosingClass())).append('.').append(c.getSimpleName());
+ } else {
+ buf.append(c.getName());
+ }
+ if (c.getTypeParameters().length > 0) {
+ buf.append('<');
+ appendAllTo(buf, ", ", c.getTypeParameters());
+ buf.append('>');
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link TypeVariable} as a {@link String}.
+ * @param v {@code TypeVariable} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String typeVariableToString(final TypeVariable> v) {
+ final StringBuilder buf = new StringBuilder(v.getName());
+ final Type[] bounds = v.getBounds();
+ if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(bounds[0]))) {
+ buf.append(" extends ");
+ appendAllTo(buf, " & ", v.getBounds());
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link ParameterizedType} as a {@link String}.
+ * @param p {@code ParameterizedType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String parameterizedTypeToString(final ParameterizedType p) {
+ final StringBuilder buf = new StringBuilder();
+
+ final Type useOwner = p.getOwnerType();
+ final Class> raw = (Class>) p.getRawType();
+ final Type[] typeArguments = p.getActualTypeArguments();
+ if (useOwner == null) {
+ buf.append(raw.getName());
+ } else {
+ if (useOwner instanceof Class>) {
+ buf.append(((Class>) useOwner).getName());
+ } else {
+ buf.append(useOwner.toString());
+ }
+ buf.append('.').append(raw.getSimpleName());
+ }
+
+ appendAllTo(buf.append('<'), ", ", typeArguments).append('>');
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link WildcardType} as a {@link String}.
+ * @param w {@code WildcardType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String wildcardTypeToString(final WildcardType w) {
+ final StringBuilder buf = new StringBuilder().append('?');
+ final Type[] lowerBounds = w.getLowerBounds();
+ final Type[] upperBounds = w.getUpperBounds();
+ if (lowerBounds.length > 1 || lowerBounds.length == 1 && lowerBounds[0] != null) {
+ appendAllTo(buf.append(" super "), " & ", lowerBounds);
+ } else if (upperBounds.length > 1 || upperBounds.length == 1 && !Object.class.equals(upperBounds[0])) {
+ appendAllTo(buf.append(" extends "), " & ", upperBounds);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link GenericArrayType} as a {@link String}.
+ * @param g {@code GenericArrayType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String genericArrayTypeToString(final GenericArrayType g) {
+ return String.format("%s[]", toString(g.getGenericComponentType()));
+ }
+
+ /**
+ * Append {@code types} to @{code buf} with separator {@code sep}.
+ * @param buf destination
+ * @param sep separator
+ * @param types to append
+ * @return {@code buf}
+ * @since 3.2
+ */
+ private static StringBuilder appendAllTo(final StringBuilder buf, final String sep, final Type... types) {
+ Validate.notEmpty(Validate.noNullElements(types));
+ if (types.length > 0) {
+ buf.append(toString(types[0]));
+ for (int i = 1; i < types.length; i++) {
+ buf.append(sep).append(toString(types[i]));
+ }
+ }
+ return buf;
+ }
+
+}
diff --git a/Java/commons-lang-TypeUtils_1080/metadata.json b/Java/commons-lang-TypeUtils_1080/metadata.json
new file mode 100644
index 000000000..760f83b03
--- /dev/null
+++ b/Java/commons-lang-TypeUtils_1080/metadata.json
@@ -0,0 +1,21 @@
+{
+ "language": "java",
+ "id": "commons-lang-TypeUtils_1080",
+ "buggyPath": ".",
+ "referencePath": null,
+ "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false",
+ "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100",
+ "categories": [
+ "safety",
+ "npe"
+ ],
+ "npe": {
+ "filepath": "src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java",
+ "line": 1085,
+ "npe_method": "getClosestParentType",
+ "deref_field": "genericInterface",
+ "npe_class": "TypeUtils",
+ "repo": "commons-lang",
+ "bug_id": "TypeUtils_1080"
+ }
+}
diff --git a/Java/commons-lang-TypeUtils_1080/npe.json b/Java/commons-lang-TypeUtils_1080/npe.json
new file mode 100644
index 000000000..421032f15
--- /dev/null
+++ b/Java/commons-lang-TypeUtils_1080/npe.json
@@ -0,0 +1,7 @@
+{
+ "filepath": "src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java",
+ "line": 1085,
+ "npe_method": "getClosestParentType",
+ "deref_field": "genericInterface",
+ "npe_class": "TypeUtils"
+}
\ No newline at end of file
diff --git a/Java/commons-lang-TypeUtils_1308/Dockerfile b/Java/commons-lang-TypeUtils_1308/Dockerfile
new file mode 100644
index 000000000..7b7fbe349
--- /dev/null
+++ b/Java/commons-lang-TypeUtils_1308/Dockerfile
@@ -0,0 +1,18 @@
+FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang
+
+ENV TZ=Asia/Seoul
+
+COPY ./metadata.json .
+COPY ./npe.json .
+COPY ./buggy.java /tmp/buggy.java
+RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \
+ && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \
+ && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \
+ && mv /tmp/buggy.java $BUGGY_PATH \
+ && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json
+
+RUN git init . && git add -A
+
+RUN $(cat metadata.json | jq -r ".buildCommand")
+
+RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi
diff --git a/Java/commons-lang-TypeUtils_1308/buggy.java b/Java/commons-lang-TypeUtils_1308/buggy.java
new file mode 100644
index 000000000..ff2072e14
--- /dev/null
+++ b/Java/commons-lang-TypeUtils_1308/buggy.java
@@ -0,0 +1,1850 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.reflect;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.lang3.builder.Builder;
+
+/**
+ * Utility methods focusing on type inspection, particularly with regard to
+ * generics.
+ *
+ * @since 3.0
+ */
+public class TypeUtils {
+
+ /**
+ * {@link WildcardType} builder.
+ * @since 3.2
+ */
+ public static class WildcardTypeBuilder implements Builder {
+ /**
+ * Constructor
+ */
+ private WildcardTypeBuilder() {
+ }
+
+ private Type[] upperBounds;
+ private Type[] lowerBounds;
+
+ /**
+ * Specify upper bounds of the wildcard type to build.
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withUpperBounds(final Type... bounds) {
+ this.upperBounds = bounds;
+ return this;
+ }
+
+ /**
+ * Specify lower bounds of the wildcard type to build.
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withLowerBounds(final Type... bounds) {
+ this.lowerBounds = bounds;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public WildcardType build() {
+ return new WildcardTypeImpl(upperBounds, lowerBounds);
+ }
+ }
+
+ /**
+ * GenericArrayType implementation class.
+ * @since 3.2
+ */
+ private static final class GenericArrayTypeImpl implements GenericArrayType {
+ private final Type componentType;
+
+ /**
+ * Constructor
+ * @param componentType of this array type
+ */
+ private GenericArrayTypeImpl(final Type componentType) {
+ this.componentType = componentType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getGenericComponentType() {
+ return componentType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof GenericArrayType && TypeUtils.equals(this, (GenericArrayType) obj);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int result = 67 << 4;
+ result |= componentType.hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * ParameterizedType implementation class.
+ * @since 3.2
+ */
+ private static final class ParameterizedTypeImpl implements ParameterizedType {
+ private final Class> raw;
+ private final Type useOwner;
+ private final Type[] typeArguments;
+
+ /**
+ * Constructor
+ * @param raw type
+ * @param useOwner owner type to use, if any
+ * @param typeArguments formal type arguments
+ */
+ private ParameterizedTypeImpl(final Class> raw, final Type useOwner, final Type[] typeArguments) {
+ this.raw = raw;
+ this.useOwner = useOwner;
+ this.typeArguments = typeArguments.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getRawType() {
+ return raw;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getOwnerType() {
+ return useOwner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type[] getActualTypeArguments() {
+ return typeArguments.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof ParameterizedType && TypeUtils.equals(this, ((ParameterizedType) obj));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings( "deprecation" ) // ObjectUtils.hashCode(Object) has been deprecated in 3.2
+ @Override
+ public int hashCode() {
+ int result = 71 << 4;
+ result |= raw.hashCode();
+ result <<= 4;
+ result |= ObjectUtils.hashCode(useOwner);
+ result <<= 8;
+ result |= Arrays.hashCode(typeArguments);
+ return result;
+ }
+ }
+
+ /**
+ * WildcardType implementation class.
+ * @since 3.2
+ */
+ private static final class WildcardTypeImpl implements WildcardType {
+ private static final Type[] EMPTY_BOUNDS = new Type[0];
+
+ private final Type[] upperBounds;
+ private final Type[] lowerBounds;
+
+ /**
+ * Constructor
+ * @param upperBounds of this type
+ * @param lowerBounds of this type
+ */
+ private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) {
+ this.upperBounds = ObjectUtils.defaultIfNull(upperBounds, EMPTY_BOUNDS);
+ this.lowerBounds = ObjectUtils.defaultIfNull(lowerBounds, EMPTY_BOUNDS);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type[] getUpperBounds() {
+ return upperBounds.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type[] getLowerBounds() {
+ return lowerBounds.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof WildcardType && TypeUtils.equals(this, (WildcardType) obj);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int result = 73 << 8;
+ result |= Arrays.hashCode(upperBounds);
+ result <<= 8;
+ result |= Arrays.hashCode(lowerBounds);
+ return result;
+ }
+ }
+
+ /**
+ * A wildcard instance matching {@code ?}.
+ * @since 3.2
+ */
+ public static final WildcardType WILDCARD_ALL = wildcardType().withUpperBounds(Object.class).build();
+
+ /**
+ * {@code TypeUtils} instances should NOT be constructed in standard
+ * programming. Instead, the class should be used as
+ * {@code TypeUtils.isAssignable(cls, toClass)}.
This
+ * constructor is public to permit tools that require a JavaBean instance to
+ * operate.
+ */
+ public TypeUtils() {
+ super();
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target type
+ * following the Java generics rules. If both types are {@link Class}
+ * objects, the method returns the result of
+ * {@link ClassUtils#isAssignable(Class, Class)}.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ public static boolean isAssignable(final Type type, final Type toType) {
+ return isAssignable(type, toType, null);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target type
+ * following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @param typeVarAssigns optional map of type variable assignments
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ private static boolean isAssignable(final Type type, final Type toType,
+ final Map, Type> typeVarAssigns) {
+ if (toType == null || toType instanceof Class>) {
+ return isAssignable(type, (Class>) toType);
+ }
+
+ if (toType instanceof ParameterizedType) {
+ return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof GenericArrayType) {
+ return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof WildcardType) {
+ return isAssignable(type, (WildcardType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof TypeVariable>) {
+ return isAssignable(type, (TypeVariable>) toType, typeVarAssigns);
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + toType);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target class
+ * following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toClass the target class
+ * @return {@code true} if {@code type} is assignable to {@code toClass}.
+ */
+ private static boolean isAssignable(final Type type, final Class> toClass) {
+ if (type == null) {
+ // consistency with ClassUtils.isAssignable() behavior
+ return toClass == null || !toClass.isPrimitive();
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toClass == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toClass.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof Class>) {
+ // just comparing two classes
+ return ClassUtils.isAssignable((Class>) type, toClass);
+ }
+
+ if (type instanceof ParameterizedType) {
+ // only have to compare the raw type to the class
+ return isAssignable(getRawType((ParameterizedType) type), toClass);
+ }
+
+ // *
+ if (type instanceof TypeVariable>) {
+ // if any of the bounds are assignable to the class, then the
+ // type is assignable to the class.
+ for (final Type bound : ((TypeVariable>) type).getBounds()) {
+ if (isAssignable(bound, toClass)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // the only classes to which a generic array type can be assigned
+ // are class Object and array classes
+ if (type instanceof GenericArrayType) {
+ return toClass.equals(Object.class)
+ || toClass.isArray()
+ && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass
+ .getComponentType());
+ }
+
+ // wildcard types are not assignable to a class (though one would think
+ // "? super Object" would be assignable to Object)
+ if (type instanceof WildcardType) {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target
+ * parameterized type following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toParameterizedType the target parameterized type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toParameterizedType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toParameterizedType.equals(type)) {
+ return true;
+ }
+
+ // get the target type's raw type
+ final Class> toClass = getRawType(toParameterizedType);
+ // get the subject type's type arguments including owner type arguments
+ // and supertype arguments up to and including the target class.
+ final Map, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
+
+ // null means the two types are not compatible
+ if (fromTypeVarAssigns == null) {
+ return false;
+ }
+
+ // compatible types, but there's no type arguments. this is equivalent
+ // to comparing Map< ?, ? > to Map, and raw types are always assignable
+ // to parameterized types.
+ if (fromTypeVarAssigns.isEmpty()) {
+ return true;
+ }
+
+ // get the target type's type arguments including owner type arguments
+ final Map, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType,
+ toClass, typeVarAssigns);
+
+ // now to check each type argument
+ for (final TypeVariable> var : toTypeVarAssigns.keySet()) {
+ final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
+ final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
+
+ if (toTypeArg == null && fromTypeArg instanceof Class) {
+ continue;
+ }
+
+ // parameters must either be absent from the subject type, within
+ // the bounds of the wildcard type, or be an exact match to the
+ // parameters of the target type.
+ if (fromTypeArg != null
+ && !toTypeArg.equals(fromTypeArg)
+ && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg,
+ typeVarAssigns))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Look up {@code var} in {@code typeVarAssigns} transitively ,
+ * i.e. keep looking until the value found is not a type variable.
+ * @param var the type variable to look up
+ * @param typeVarAssigns the map used for the look up
+ * @return Type or {@code null} if some variable was not in the map
+ * @since 3.2
+ */
+ private static Type unrollVariableAssignments(TypeVariable> var, final Map, Type> typeVarAssigns) {
+ Type result;
+ do {
+ result = typeVarAssigns.get(var);
+ if (result instanceof TypeVariable> && !result.equals(var)) {
+ var = (TypeVariable>) result;
+ continue;
+ }
+ break;
+ } while (true);
+ return result;
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target
+ * generic array type following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toGenericArrayType the target generic array type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toGenericArrayType}.
+ */
+ private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toGenericArrayType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toGenericArrayType.equals(type)) {
+ return true;
+ }
+
+ final Type toComponentType = toGenericArrayType.getGenericComponentType();
+
+ if (type instanceof Class>) {
+ final Class> cls = (Class>) type;
+
+ // compare the component types
+ return cls.isArray()
+ && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
+ }
+
+ if (type instanceof GenericArrayType) {
+ // compare the component types
+ return isAssignable(((GenericArrayType) type).getGenericComponentType(),
+ toComponentType, typeVarAssigns);
+ }
+
+ if (type instanceof WildcardType) {
+ // so long as one of the upper bounds is assignable, it's good
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (type instanceof TypeVariable>) {
+ // probably should remove the following logic and just return false.
+ // type variables cannot specify arrays as bounds.
+ for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (type instanceof ParameterizedType) {
+ // the raw type of a parameterized type is never an array or
+ // generic array, otherwise the declaration would look like this:
+ // Collection[]< ? extends String > collection;
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target
+ * wildcard type following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toWildcardType the target wildcard type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toWildcardType}.
+ */
+ private static boolean isAssignable(final Type type, final WildcardType toWildcardType,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toWildcardType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toWildcardType.equals(type)) {
+ return true;
+ }
+
+ final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
+ final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
+
+ if (type instanceof WildcardType) {
+ final WildcardType wildcardType = (WildcardType) type;
+ final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
+ final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
+
+ for (Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
+
+ // each upper bound of the subject type has to be assignable to
+ // each
+ // upper bound of the target type
+ for (final Type bound : upperBounds) {
+ if (!isAssignable(bound, toBound, typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+
+ for (Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
+
+ // each lower bound of the target type has to be assignable to
+ // each
+ // lower bound of the subject type
+ for (final Type bound : lowerBounds) {
+ if (!isAssignable(toBound, bound, typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ for (final Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns),
+ typeVarAssigns)) {
+ return false;
+ }
+ }
+
+ for (final Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type,
+ typeVarAssigns)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the subject type may be implicitly cast to the target type
+ * variable following the Java generics rules.
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toTypeVariable the target type variable
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toTypeVariable}.
+ */
+ private static boolean isAssignable(final Type type, final TypeVariable> toTypeVariable,
+ final Map, Type> typeVarAssigns) {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toTypeVariable == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toTypeVariable.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof TypeVariable>) {
+ // a type variable is assignable to another type variable, if
+ // and only if the former is the latter, extends the latter, or
+ // is otherwise a descendant of the latter.
+ final Type[] bounds = getImplicitBounds((TypeVariable>) type);
+
+ for (final Type bound : bounds) {
+ if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
+ return true;
+ }
+ }
+ }
+
+ if (type instanceof Class> || type instanceof ParameterizedType
+ || type instanceof GenericArrayType || type instanceof WildcardType) {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Find the mapping for {@code type} in {@code typeVarAssigns}.
+ *
+ * @param type the type to be replaced
+ * @param typeVarAssigns the map with type variables
+ * @return the replaced type
+ * @throws IllegalArgumentException if the type cannot be substituted
+ */
+ private static Type substituteTypeVariables(final Type type, final Map, Type> typeVarAssigns) {
+ if (type instanceof TypeVariable> && typeVarAssigns != null) {
+ final Type replacementType = typeVarAssigns.get(type);
+
+ if (replacementType == null) {
+ throw new IllegalArgumentException("missing assignment type for type variable "
+ + type);
+ }
+ return replacementType;
+ }
+ return type;
+ }
+
+ /**
+ * Retrieves all the type arguments for this parameterized type
+ * including owner hierarchy arguments such as
+ * {@code Outer.Inner.DeepInner} .
+ * The arguments are returned in a
+ * {@link Map} specifying the argument type for each {@link TypeVariable}.
+ *
+ *
+ * @param type specifies the subject parameterized type from which to
+ * harvest the parameters.
+ * @return a {@code Map} of the type arguments to their respective type
+ * variables.
+ */
+ public static Map, Type> getTypeArguments(final ParameterizedType type) {
+ return getTypeArguments(type, getRawType(type), null);
+ }
+
+ /**
+ * Gets the type arguments of a class/interface based on a subtype. For
+ * instance, this method will determine that both of the parameters for the
+ * interface {@link Map} are {@link Object} for the subtype
+ * {@link java.util.Properties Properties} even though the subtype does not
+ * directly implement the {@code Map} interface.
+ * This method returns {@code null} if {@code type} is not assignable to
+ * {@code toClass}. It returns an empty map if none of the classes or
+ * interfaces in its inheritance hierarchy specify any type arguments.
+ * A side effect of this method is that it also retrieves the type
+ * arguments for the classes and interfaces that are part of the hierarchy
+ * between {@code type} and {@code toClass}. So with the above
+ * example, this method will also determine that the type arguments for
+ * {@link java.util.Hashtable Hashtable} are also both {@code Object}.
+ * In cases where the interface specified by {@code toClass} is
+ * (indirectly) implemented more than once (e.g. where {@code toClass}
+ * specifies the interface {@link java.lang.Iterable Iterable} and
+ * {@code type} specifies a parameterized type that implements both
+ * {@link java.util.Set Set} and {@link java.util.Collection Collection}),
+ * this method will look at the inheritance hierarchy of only one of the
+ * implementations/subclasses; the first interface encountered that isn't a
+ * subinterface to one of the others in the {@code type} to
+ * {@code toClass} hierarchy.
+ *
+ * @param type the type from which to determine the type parameters of
+ * {@code toClass}
+ * @param toClass the class whose type parameters are to be determined based
+ * on the subtype {@code type}
+ * @return a {@code Map} of the type assignments for the type variables in
+ * each type in the inheritance hierarchy from {@code type} to
+ * {@code toClass} inclusive.
+ */
+ public static Map, Type> getTypeArguments(final Type type, final Class> toClass) {
+ return getTypeArguments(type, toClass, null);
+ }
+
+ /**
+ * Return a map of the type arguments of @{code type} in the context of {@code toClass}.
+ *
+ * @param type the type in question
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(final Type type, final Class> toClass,
+ final Map, Type> subtypeVarAssigns) {
+ if (type instanceof Class>) {
+ return getTypeArguments((Class>) type, toClass, subtypeVarAssigns);
+ }
+
+ if (type instanceof ParameterizedType) {
+ return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns);
+ }
+
+ if (type instanceof GenericArrayType) {
+ return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass
+ .isArray() ? toClass.getComponentType() : toClass, subtypeVarAssigns);
+ }
+
+ // since wildcard types are not assignable to classes, should this just
+ // return null?
+ if (type instanceof WildcardType) {
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ // find the first bound that is assignable to the target class
+ if (isAssignable(bound, toClass)) {
+ return getTypeArguments(bound, toClass, subtypeVarAssigns);
+ }
+ }
+
+ return null;
+ }
+
+ if (type instanceof TypeVariable>) {
+ for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
+ // find the first bound that is assignable to the target class
+ if (isAssignable(bound, toClass)) {
+ return getTypeArguments(bound, toClass, subtypeVarAssigns);
+ }
+ }
+
+ return null;
+ }
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ * Return a map of the type arguments of a parameterized type in the context of {@code toClass}.
+ *
+ * @param parameterizedType the parameterized type
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(
+ final ParameterizedType parameterizedType, final Class> toClass,
+ final Map, Type> subtypeVarAssigns) {
+ final Class> cls = getRawType(parameterizedType);
+
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ final Type ownerType = parameterizedType.getOwnerType();
+ Map, Type> typeVarAssigns;
+
+ if (ownerType instanceof ParameterizedType) {
+ // get the owner type arguments first
+ final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
+ typeVarAssigns = getTypeArguments(parameterizedOwnerType,
+ getRawType(parameterizedOwnerType), subtypeVarAssigns);
+ } else {
+ // no owner, prep the type variable assignments map
+ typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>()
+ : new HashMap, Type>(subtypeVarAssigns);
+ }
+
+ // get the subject parameterized type's arguments
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+ // and get the corresponding type variables from the raw class
+ final TypeVariable>[] typeParams = cls.getTypeParameters();
+
+ // map the arguments to their respective type variables
+ for (int i = 0; i < typeParams.length; i++) {
+ final Type typeArg = typeArgs[i];
+ typeVarAssigns.put(typeParams[i], typeVarAssigns.containsKey(typeArg) ? typeVarAssigns
+ .get(typeArg) : typeArg);
+ }
+
+ if (toClass.equals(cls)) {
+ // target class has been reached. Done.
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
+ }
+
+ /**
+ * Return a map of the type arguments of a class in the context of @{code toClass}.
+ *
+ * @param cls the class in question
+ * @param toClass the context class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(Class> cls, final Class> toClass,
+ final Map, Type> subtypeVarAssigns) {
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ // can't work with primitives
+ if (cls.isPrimitive()) {
+ // both classes are primitives?
+ if (toClass.isPrimitive()) {
+ // dealing with widening here. No type arguments to be
+ // harvested with these two types.
+ return new HashMap, Type>();
+ }
+
+ // work with wrapper the wrapper class instead of the primitive
+ cls = ClassUtils.primitiveToWrapper(cls);
+ }
+
+ // create a copy of the incoming map, or an empty one if it's null
+ final HashMap, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>()
+ : new HashMap, Type>(subtypeVarAssigns);
+
+ // has target class been reached?
+ if (toClass.equals(cls)) {
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
+ }
+
+ /**
+ * Tries to determine the type arguments of a class/interface based on a
+ * super parameterized type's type arguments. This method is the inverse of
+ * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
+ * type arguments based on a subtype. It is far more limited in determining
+ * the type arguments for the subject class's type variables in that it can
+ * only determine those parameters that map from the subject {@link Class}
+ * object to the supertype.
Example: {@link java.util.TreeSet
+ * TreeSet} sets its parameter as the parameter for
+ * {@link java.util.NavigableSet NavigableSet}, which in turn sets the
+ * parameter of {@link java.util.SortedSet}, which in turn sets the
+ * parameter of {@link Set}, which in turn sets the parameter of
+ * {@link java.util.Collection}, which in turn sets the parameter of
+ * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps
+ * (indirectly) to {@code Iterable}'s parameter, it will be able to
+ * determine that based on the super type {@code Iterable extends
+ * Map>>}, the parameter of
+ * {@code TreeSet} is {@code ? extends Map>}.
+ *
+ * @param cls the class whose type parameters are to be determined, not {@code null}
+ * @param superType the super type from which {@code cls}'s type
+ * arguments are to be determined, not {@code null}
+ * @return a {@code Map} of the type assignments that could be determined
+ * for the type variables in each type in the inheritance hierarchy from
+ * {@code type} to {@code toClass} inclusive.
+ */
+ public static Map, Type> determineTypeArguments(final Class> cls,
+ final ParameterizedType superType) {
+ Validate.notNull(cls, "cls is null");
+ Validate.notNull(superType, "superType is null");
+
+ final Class> superClass = getRawType(superType);
+
+ // compatibility check
+ if (!isAssignable(cls, superClass)) {
+ return null;
+ }
+
+ if (cls.equals(superClass)) {
+ return getTypeArguments(superType, superClass, null);
+ }
+
+ // get the next class in the inheritance hierarchy
+ final Type midType = getClosestParentType(cls, superClass);
+
+ // can only be a class or a parameterized type
+ if (midType instanceof Class>) {
+ return determineTypeArguments((Class>) midType, superType);
+ }
+
+ final ParameterizedType midParameterizedType = (ParameterizedType) midType;
+ final Class> midClass = getRawType(midParameterizedType);
+ // get the type variables of the mid class that map to the type
+ // arguments of the super class
+ final Map, Type> typeVarAssigns = determineTypeArguments(midClass, superType);
+ // map the arguments of the mid type to the class type variables
+ mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
+
+ return typeVarAssigns;
+ }
+
+ /**
+ * Performs a mapping of type variables.
+ *
+ * @param the generic type of the class in question
+ * @param cls the class in question
+ * @param parameterizedType the parameterized type
+ * @param typeVarAssigns the map to be filled
+ */
+ private static void mapTypeVariablesToArguments(final Class cls,
+ final ParameterizedType parameterizedType, final Map, Type> typeVarAssigns) {
+ // capture the type variables from the owner type that have assignments
+ final Type ownerType = parameterizedType.getOwnerType();
+
+ if (ownerType instanceof ParameterizedType) {
+ // recursion to make sure the owner's owner type gets processed
+ mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
+ }
+
+ // parameterizedType is a generic interface/class (or it's in the owner
+ // hierarchy of said interface/class) implemented/extended by the class
+ // cls. Find out which type variables of cls are type arguments of
+ // parameterizedType:
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+
+ // of the cls's type variables that are arguments of parameterizedType,
+ // find out which ones can be determined from the super type's arguments
+ final TypeVariable>[] typeVars = getRawType(parameterizedType).getTypeParameters();
+
+ // use List view of type parameters of cls so the contains() method can be used:
+ final List>> typeVarList = Arrays.asList(cls
+ .getTypeParameters());
+
+ for (int i = 0; i < typeArgs.length; i++) {
+ final TypeVariable> typeVar = typeVars[i];
+ final Type typeArg = typeArgs[i];
+
+ // argument of parameterizedType is a type variable of cls
+ if (typeVarList.contains(typeArg)
+ // type variable of parameterizedType has an assignment in
+ // the super type.
+ && typeVarAssigns.containsKey(typeVar)) {
+ // map the assignment to the cls's type variable
+ typeVarAssigns.put((TypeVariable>) typeArg, typeVarAssigns.get(typeVar));
+ }
+ }
+ }
+
+ /**
+ * Get the closest parent type to the
+ * super class specified by {@code superClass}.
+ *
+ * @param cls the class in question
+ * @param superClass the super class
+ * @return the closes parent type
+ */
+ private static Type getClosestParentType(final Class> cls, final Class> superClass) {
+ // only look at the interfaces if the super class is also an interface
+ if (superClass.isInterface()) {
+ // get the generic interfaces of the subject class
+ final Type[] interfaceTypes = cls.getGenericInterfaces();
+ // will hold the best generic interface match found
+ Type genericInterface = null;
+
+ // find the interface closest to the super class
+ for (final Type midType : interfaceTypes) {
+ Class> midClass = null;
+
+ if (midType instanceof ParameterizedType) {
+ midClass = getRawType((ParameterizedType) midType);
+ } else if (midType instanceof Class>) {
+ midClass = (Class>) midType;
+ } else {
+ throw new IllegalStateException("Unexpected generic"
+ + " interface type found: " + midType);
+ }
+
+ // check if this interface is further up the inheritance chain
+ // than the previously found match
+ if (isAssignable(midClass, superClass)
+ && isAssignable(genericInterface, (Type) midClass)) {
+ genericInterface = midType;
+ }
+ }
+
+ // found a match?
+ if (genericInterface != null) {
+ return genericInterface;
+ }
+ }
+
+ // none of the interfaces were descendants of the target class, so the
+ // super class has to be one, instead
+ return cls.getGenericSuperclass();
+ }
+
+ /**
+ * Checks if the given value can be assigned to the target type
+ * following the Java generics rules.
+ *
+ * @param value the value to be checked
+ * @param type the target type
+ * @return {@code true} if {@code value} is an instance of {@code type}.
+ */
+ public static boolean isInstance(final Object value, final Type type) {
+ if (type == null) {
+ return false;
+ }
+
+ return value == null ? !(type instanceof Class>) || !((Class>) type).isPrimitive()
+ : isAssignable(value.getClass(), type, null);
+ }
+
+ /**
+ * This method strips out the redundant upper bound types in type
+ * variable types and wildcard types (or it would with wildcard types if
+ * multiple upper bounds were allowed).
Example, with the variable
+ * type declaration:
+ *
+ *
<K extends java.util.Collection<String> &
+ * java.util.List<String>>
+ *
+ *
+ * since {@code List} is a subinterface of {@code Collection},
+ * this method will return the bounds as if the declaration had been:
+ *
+ *
+ * <K extends java.util.List<String>>
+ *
+ * @param bounds an array of types representing the upper bounds of either
+ * {@link WildcardType} or {@link TypeVariable}, not {@code null}.
+ * @return an array containing the values from {@code bounds} minus the
+ * redundant types.
+ */
+ public static Type[] normalizeUpperBounds(final Type[] bounds) {
+ Validate.notNull(bounds, "null value specified for bounds array");
+ // don't bother if there's only one (or none) type
+ if (bounds.length < 2) {
+ return bounds;
+ }
+
+ final Set types = new HashSet(bounds.length);
+
+ for (final Type type1 : bounds) {
+ boolean subtypeFound = false;
+
+ for (final Type type2 : bounds) {
+ if (type1 != type2 && isAssignable(type2, type1, null)) {
+ subtypeFound = true;
+ break;
+ }
+ }
+
+ if (!subtypeFound) {
+ types.add(type1);
+ }
+ }
+
+ return types.toArray(new Type[types.size()]);
+ }
+
+ /**
+ * Returns an array containing the sole type of {@link Object} if
+ * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
+ * returns the result of {@link TypeVariable#getBounds()} passed into
+ * {@link #normalizeUpperBounds}.
+ *
+ * @param typeVariable the subject type variable, not {@code null}
+ * @return a non-empty array containing the bounds of the type variable.
+ */
+ public static Type[] getImplicitBounds(final TypeVariable> typeVariable) {
+ Validate.notNull(typeVariable, "typeVariable is null");
+ final Type[] bounds = typeVariable.getBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ * Returns an array containing the sole value of {@link Object} if
+ * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getUpperBounds()}
+ * passed into {@link #normalizeUpperBounds}.
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the upper bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) {
+ Validate.notNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getUpperBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ * Returns an array containing a single value of {@code null} if
+ * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getLowerBounds()}.
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the lower bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) {
+ Validate.notNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getLowerBounds();
+
+ return bounds.length == 0 ? new Type[] { null } : bounds;
+ }
+
+ /**
+ * Determines whether or not specified types satisfy the bounds of their
+ * mapped type variables. When a type parameter extends another (such as
+ * {@code