diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4ed8fb1..0a6bf328 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,10 @@ Breaking API changes:
* See [#768](https://github.com/eclipse-lsp4j/lsp4j/issues/768) for detailed discussion.
* The name field in WorkspaceFolder is no longer optional according to the specification.
* See [#741](https://github.com/eclipse-lsp4j/lsp4j/issues/741) for detailed discussion.
+ * The LSP4J generator when applied to `@JsonRpcData` annotated classes generates a dependency on package `org.eclipse.lsp4j.jsonrpc.util` in the `org.eclipse.lsp4j.jsonrpc` bundle to provide an implementation of `ToStringBuilder`.
+ * This removes the implied requirement in LSP4J 0.21.0 that there is a class called `ToStringBuilder` in a sub-package called `util`.
+ * This also removes the duplicated class `ToStringBuilder` in packages `org.eclipse.lsp4j.debug.util` and `org.eclipse.lsp4j.util`.
+ * See [#742](https://github.com/eclipse-lsp4j/lsp4j/issues/742) for detailed discussion.
Nightly japicmp report:
diff --git a/org.eclipse.lsp4j.debug/src/main/java/org/eclipse/lsp4j/debug/util/ToStringBuilder.java b/org.eclipse.lsp4j.debug/src/main/java/org/eclipse/lsp4j/debug/util/ToStringBuilder.java
deleted file mode 100644
index 513b4325..00000000
--- a/org.eclipse.lsp4j.debug/src/main/java/org/eclipse/lsp4j/debug/util/ToStringBuilder.java
+++ /dev/null
@@ -1,372 +0,0 @@
-package org.eclipse.lsp4j.debug.util;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.List;
-
-
-/**
- * Helps with the construction of good {@link Object#toString()} representations.
- *
You can customize the output using the builder-style methods {@link ToStringBuilder#singleLine()} {@link ToStringBuilder#skipNulls()} and {@link ToStringBuilder#hideFieldNames()}.
- * You can either directly list fields to include via {@link ToStringBuilder#add(String, Object)} and {@link ToStringBuilder#add(Object)}
- * or you can let the builder do it automatically using reflection, either including the fields declared in this class or including all superclasses.
- * The builder will automatically handle cycles in the object tree. It also pretty prints arrays and Iterables.
- *
- * This class is not thread safe.
- * @since 2.7
- */
-public final class ToStringBuilder {
-
- public static class ToStringContext {
-
- public final static ToStringContext INSTANCE = new ToStringContext();
-
- private final static ThreadLocal> currentlyProcessed = new ThreadLocal>() {
- @Override
- public IdentityHashMap initialValue() {
- return new IdentityHashMap();
- }
- };
-
- public boolean startProcessing(final Object obj) {
- return ToStringContext.currentlyProcessed.get().put(obj, Boolean.TRUE) == null;
- }
-
- public void endProcessing(final Object obj) {
- ToStringContext.currentlyProcessed.get().remove(obj);
- }
- }
-
- private static ToStringContext toStringContext = ToStringContext.INSTANCE;
-
- private final Object instance;
-
- private final String typeName;
-
- private boolean multiLine = true;
-
- private boolean skipNulls = false;
-
- private boolean showFieldNames = true;
-
- private boolean prettyPrint = true;
-
- private final List parts = new ArrayList();
-
- /**
- * Creates a new ToStringBuilder for the given object. If you don't use reflection, then this instance
- * is only used for obtaining its classes' simple name.
- *
- * @param instance the object to convert to a String
- */
- public ToStringBuilder(final Object instance) {
- this.instance = instance;
- this.typeName = instance.getClass().getSimpleName();
- }
-
- /**
- * Fields are printed on a single line, separated by commas instead of newlines
- * @return this
- */
- public ToStringBuilder singleLine() {
- this.multiLine = false;
- return this;
- }
-
- /**
- * Fields with null values will be excluded from the output
- * @return this
- */
- public ToStringBuilder skipNulls() {
- this.skipNulls = true;
- return this;
- }
-
- /**
- * Field names will not be included in the output. Useful for small classes.
- * @return this
- */
- public ToStringBuilder hideFieldNames() {
- this.showFieldNames = false;
- return this;
- }
-
- /**
- * By default, Iterables, Arrays and multiline Strings are pretty-printed.
- * Switching to their normal representation makes the toString method significantly faster.
- * @since 2.9
- * @return this
- */
- public ToStringBuilder verbatimValues() {
- this.prettyPrint = false;
- return this;
- }
-
- /**
- * Adds all fields declared directly in the object's class to the output
- * @return this
- */
- public ToStringBuilder addDeclaredFields() {
- Field[] fields = instance.getClass().getDeclaredFields();
- for(Field field : fields) {
- addField(field);
- }
- return this;
- }
-
- /**
- * Adds all fields declared in the object's class and its superclasses to the output.
- * @return this
- */
- public ToStringBuilder addAllFields() {
- List fields = getAllDeclaredFields(instance.getClass());
- for(Field field : fields) {
- addField(field);
- }
- return this;
- }
-
- /**
- * @param fieldName the name of the field to add to the output using reflection
- * @return this
- */
- public ToStringBuilder addField(final String fieldName) {
- List fields = getAllDeclaredFields(instance.getClass());
- for(Field field : fields) {
- if(fieldName.equals(field.getName())) {
- addField(field);
- break;
- }
- }
- return this;
- }
-
- private ToStringBuilder addField(final Field field) {
- if (!Modifier.isStatic(field.getModifiers())) {
- field.setAccessible(true);
- try {
- add(field.getName(), field.get(instance));
- } catch(IllegalAccessException e) {
- sneakyThrow(e);
- }
- }
- return this;
- }
-
- @SuppressWarnings("unchecked")
- private static void sneakyThrow(Throwable t) throws T {
- throw (T) t;
- }
-
- /**
- * @param value the value to add to the output
- * @param fieldName the field name to list the value under
- * @return this
- */
- public ToStringBuilder add(final String fieldName, final Object value) {
- return addPart(fieldName, value);
- }
-
- /**
- * @param value the value to add to the output without a field name
- * @return this
- */
- public ToStringBuilder add(final Object value) {
- return addPart(value);
- }
-
- private Part addPart() {
- final Part p = new Part();
- this.parts.add(p);
- return p;
- }
-
- private ToStringBuilder addPart(final Object value) {
- final Part p = this.addPart();
- p.value = value;
- return this;
- }
-
- private ToStringBuilder addPart(final String fieldName, final Object value) {
- final Part p = this.addPart();
- p.fieldName = fieldName;
- p.value = value;
- return this;
- }
-
- /**
- * @return the String representation of the processed object
- */
- @Override
- public String toString() {
- boolean startProcessing = ToStringBuilder.toStringContext.startProcessing(this.instance);
- if (!startProcessing) {
- return this.toSimpleReferenceString(this.instance);
- }
- try {
- final IndentationAwareStringBuilder builder = new IndentationAwareStringBuilder();
- builder.append(typeName).append(" ");
- builder.append("[");
- String nextSeparator = "";
- if (multiLine) {
- builder.increaseIndent();
- }
- for (Part part : parts) {
- if (!skipNulls || part.value != null) {
- if (multiLine) {
- builder.newLine();
- } else {
- builder.append(nextSeparator);
- nextSeparator = ", ";
- }
- if (part.fieldName != null && this.showFieldNames) {
- builder.append(part.fieldName).append(" = ");
- }
- this.internalToString(part.value, builder);
- }
- }
- if (multiLine) {
- builder.decreaseIndent().newLine();
- }
- builder.append("]");
- return builder.toString();
- } finally {
- ToStringBuilder.toStringContext.endProcessing(this.instance);
- }
- }
-
- private void internalToString(final Object object, final IndentationAwareStringBuilder sb) {
- if (prettyPrint) {
- if (object instanceof Iterable>) {
- serializeIterable((Iterable>)object, sb);
- } else if (object instanceof Object[]) {
- sb.append(Arrays.toString((Object[])object));
- } else if (object instanceof byte[]) {
- sb.append(Arrays.toString((byte[])object));
- } else if (object instanceof char[]) {
- sb.append(Arrays.toString((char[])object));
- } else if (object instanceof int[]) {
- sb.append(Arrays.toString((int[])object));
- } else if (object instanceof boolean[]) {
- sb.append(Arrays.toString((boolean[])object));
- } else if (object instanceof long[]) {
- sb.append(Arrays.toString((long[])object));
- } else if (object instanceof float[]) {
- sb.append(Arrays.toString((float[])object));
- } else if (object instanceof double[]) {
- sb.append(Arrays.toString((double[])object));
- } else if (object instanceof CharSequence) {
- sb.append("\"").append(((CharSequence)object).toString().replace("\n", "\\n").replace("\r", "\\r")).append("\"");
- } else if (object instanceof Enum>) {
- sb.append(((Enum>)object).name());
- } else {
- sb.append(String.valueOf(object));
- }
- } else {
- sb.append(String.valueOf(object));
- }
- }
-
- private void serializeIterable(final Iterable> object, final IndentationAwareStringBuilder sb) {
- final Iterator> iterator = object.iterator();
- sb.append(object.getClass().getSimpleName()).append(" (");
- if (multiLine) {
- sb.increaseIndent();
- }
- boolean wasEmpty = true;
- while (iterator.hasNext()) {
- wasEmpty = false;
- if (multiLine) {
- sb.newLine();
- }
- this.internalToString(iterator.next(), sb);
- if (iterator.hasNext()) {
- sb.append(",");
- }
- }
- if (multiLine) {
- sb.decreaseIndent();
- }
- if (!wasEmpty && this.multiLine) {
- sb.newLine();
- }
- sb.append(")");
- }
-
- private String toSimpleReferenceString(final Object obj) {
- String simpleName = obj.getClass().getSimpleName();
- int identityHashCode = System.identityHashCode(obj);
- return simpleName + "@" + Integer.valueOf(identityHashCode);
- }
-
- private List getAllDeclaredFields(final Class> clazz) {
- final ArrayList result = new ArrayList<>();
-
- for(Class> current = clazz; current != null; current = current.getSuperclass()) {
- Field[] declaredFields = current.getDeclaredFields();
- result.addAll(Arrays.asList(declaredFields));
-
- }
- return result;
- }
-
- private static final class Part {
- private String fieldName;
- private Object value;
- }
-
- private static class IndentationAwareStringBuilder {
- private final StringBuilder builder = new StringBuilder();
-
- private final String indentationString = " ";
-
- private final String newLineString = "\n";
-
- private int indentation = 0;
-
- public IndentationAwareStringBuilder increaseIndent() {
- indentation++;
- return this;
- }
-
- public IndentationAwareStringBuilder decreaseIndent() {
- indentation--;
- return this;
- }
-
- public IndentationAwareStringBuilder append(final CharSequence string) {
- if (indentation > 0) {
- String indented = string.toString().replace(
- newLineString,
- newLineString + repeat(indentationString, indentation)
- );
- builder.append(indented);
- } else {
- builder.append(string);
- }
- return this;
- }
-
- public IndentationAwareStringBuilder newLine() {
- builder.append(newLineString).
- append(repeat(this.indentationString, this.indentation));
- return this;
- }
-
- @Override
- public String toString() {
- return this.builder.toString();
- }
-
- private String repeat(String string, int count) {
- StringBuilder result = new StringBuilder();
- for(int i=0; i < count; i++) {
- result.append(string);
- }
- return result.toString();
- }
- }
-}
\ No newline at end of file
diff --git a/org.eclipse.lsp4j.generator/src/main/java/org/eclipse/lsp4j/generator/JsonRpcDataProcessor.xtend b/org.eclipse.lsp4j.generator/src/main/java/org/eclipse/lsp4j/generator/JsonRpcDataProcessor.xtend
index fef82ee2..19e32d42 100644
--- a/org.eclipse.lsp4j.generator/src/main/java/org/eclipse/lsp4j/generator/JsonRpcDataProcessor.xtend
+++ b/org.eclipse.lsp4j.generator/src/main/java/org/eclipse/lsp4j/generator/JsonRpcDataProcessor.xtend
@@ -159,10 +159,7 @@ class JsonRpcDataProcessor extends AbstractClassProcessor {
returnType = string
addAnnotation(newAnnotationReference(Override))
val accessorsUtil = new AccessorsProcessor.Util(context)
- val fqn = impl.qualifiedName
- val char dot = '.'
- val pkg = fqn.substring(0, fqn.lastIndexOf(dot))
- val toStringBuilderClassName = (pkg+".util.ToStringBuilder")
+ val toStringBuilderClassName = ("org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder")
body = '''
«toStringBuilderClassName.newTypeReference()» b = new «toStringBuilderClassName.newTypeReference()»(this);
«FOR field : toStringFields»
diff --git a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/CancelParams.java b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/CancelParams.java
index c38bb25e..727cb0a9 100644
--- a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/CancelParams.java
+++ b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/CancelParams.java
@@ -12,6 +12,7 @@
package org.eclipse.lsp4j.jsonrpc.messages;
import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import com.google.gson.JsonIOException;
diff --git a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/Message.java b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/Message.java
index 7e51dc51..15063ed7 100644
--- a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/Message.java
+++ b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/Message.java
@@ -15,6 +15,7 @@
import org.eclipse.lsp4j.jsonrpc.json.MessageConstants;
import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import com.google.gson.JsonIOException;
diff --git a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ResponseError.java b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ResponseError.java
index 1c1033b8..2dcd919a 100644
--- a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ResponseError.java
+++ b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ResponseError.java
@@ -13,6 +13,7 @@
import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler;
import org.eclipse.lsp4j.jsonrpc.json.adapters.JsonElementTypeAdapter;
+import org.eclipse.lsp4j.jsonrpc.util.ToStringBuilder;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import com.google.gson.JsonIOException;
diff --git a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ToStringBuilder.java b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/util/ToStringBuilder.java
similarity index 97%
rename from org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ToStringBuilder.java
rename to org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/util/ToStringBuilder.java
index 0ece764c..02252840 100644
--- a/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/messages/ToStringBuilder.java
+++ b/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/util/ToStringBuilder.java
@@ -1,12 +1,12 @@
/**
- * Copyright (c) 2014, 2018 itemis AG (http://www.itemis.eu) and others.
+ * Copyright (c) 2014, 2024 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.eclipse.lsp4j.jsonrpc.messages;
+package org.eclipse.lsp4j.jsonrpc.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -25,8 +25,10 @@
* The builder will automatically handle cycles in the object tree. It also pretty prints arrays and Iterables.
*
* This class is not thread safe.
+ *
+ * @implNote This class originally came from xbase.lib and has been extended for LSP4J purposes with some additional functionality.
*/
-final class ToStringBuilder {
+public final class ToStringBuilder {
public static class ToStringContext {
diff --git a/org.eclipse.lsp4j/src/main/java/org/eclipse/lsp4j/util/ToStringBuilder.java b/org.eclipse.lsp4j/src/main/java/org/eclipse/lsp4j/util/ToStringBuilder.java
deleted file mode 100644
index 6276f595..00000000
--- a/org.eclipse.lsp4j/src/main/java/org/eclipse/lsp4j/util/ToStringBuilder.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/**
- * Copyright (c) 2014, 2018 itemis AG (http://www.itemis.eu) and others.
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.eclipse.lsp4j.util;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.List;
-
-
-/**
- * Helps with the construction of good {@link Object#toString()} representations.
- * You can customize the output using the builder-style methods {@link ToStringBuilder#singleLine()} {@link ToStringBuilder#skipNulls()} and {@link ToStringBuilder#hideFieldNames()}.
- * You can either directly list fields to include via {@link ToStringBuilder#add(String, Object)} and {@link ToStringBuilder#add(Object)}
- * or you can let the builder do it automatically using reflection, either including the fields declared in this class or including all superclasses.
- * The builder will automatically handle cycles in the object tree. It also pretty prints arrays and Iterables.
- *
- * This class is not thread safe.
- * @since 2.7
- */
-public final class ToStringBuilder {
-
- public static class ToStringContext {
-
- public final static ToStringContext INSTANCE = new ToStringContext();
-
- private final static ThreadLocal> currentlyProcessed = new ThreadLocal>() {
- @Override
- public IdentityHashMap initialValue() {
- return new IdentityHashMap();
- }
- };
-
- public boolean startProcessing(final Object obj) {
- return ToStringContext.currentlyProcessed.get().put(obj, Boolean.TRUE) == null;
- }
-
- public void endProcessing(final Object obj) {
- ToStringContext.currentlyProcessed.get().remove(obj);
- }
- }
-
- private static ToStringContext toStringContext = ToStringContext.INSTANCE;
-
- private final Object instance;
-
- private final String typeName;
-
- private boolean multiLine = true;
-
- private boolean skipNulls = false;
-
- private boolean showFieldNames = true;
-
- private boolean prettyPrint = true;
-
- private final List parts = new ArrayList();
-
- /**
- * Creates a new ToStringBuilder for the given object. If you don't use reflection, then this instance
- * is only used for obtaining its classes' simple name.
- *
- * @param instance the object to convert to a String
- */
- public ToStringBuilder(final Object instance) {
- this.instance = instance;
- this.typeName = instance.getClass().getSimpleName();
- }
-
- /**
- * Fields are printed on a single line, separated by commas instead of newlines
- * @return this
- */
- public ToStringBuilder singleLine() {
- this.multiLine = false;
- return this;
- }
-
- /**
- * Fields with null values will be excluded from the output
- * @return this
- */
- public ToStringBuilder skipNulls() {
- this.skipNulls = true;
- return this;
- }
-
- /**
- * Field names will not be included in the output. Useful for small classes.
- * @return this
- */
- public ToStringBuilder hideFieldNames() {
- this.showFieldNames = false;
- return this;
- }
-
- /**
- * By default, Iterables, Arrays and multiline Strings are pretty-printed.
- * Switching to their normal representation makes the toString method significantly faster.
- * @since 2.9
- * @return this
- */
- public ToStringBuilder verbatimValues() {
- this.prettyPrint = false;
- return this;
- }
-
- /**
- * Adds all fields declared directly in the object's class to the output
- * @return this
- */
- public ToStringBuilder addDeclaredFields() {
- Field[] fields = instance.getClass().getDeclaredFields();
- for(Field field : fields) {
- addField(field);
- }
- return this;
- }
-
- /**
- * Adds all fields declared in the object's class and its superclasses to the output.
- * @return this
- */
- public ToStringBuilder addAllFields() {
- List fields = getAllDeclaredFields(instance.getClass());
- for(Field field : fields) {
- addField(field);
- }
- return this;
- }
-
- /**
- * @param fieldName the name of the field to add to the output using reflection
- * @return this
- */
- public ToStringBuilder addField(final String fieldName) {
- List fields = getAllDeclaredFields(instance.getClass());
- for(Field field : fields) {
- if(fieldName.equals(field.getName())) {
- addField(field);
- break;
- }
- }
- return this;
- }
-
- private ToStringBuilder addField(final Field field) {
- if (!Modifier.isStatic(field.getModifiers())) {
- field.setAccessible(true);
- try {
- add(field.getName(), field.get(instance));
- } catch(IllegalAccessException e) {
- sneakyThrow(e);
- }
- }
- return this;
- }
-
- @SuppressWarnings("unchecked")
- private static void sneakyThrow(Throwable t) throws T {
- throw (T) t;
- }
-
- /**
- * @param value the value to add to the output
- * @param fieldName the field name to list the value under
- * @return this
- */
- public ToStringBuilder add(final String fieldName, final Object value) {
- return addPart(fieldName, value);
- }
-
- /**
- * @param value the value to add to the output without a field name
- * @return this
- */
- public ToStringBuilder add(final Object value) {
- return addPart(value);
- }
-
- private Part addPart() {
- final Part p = new Part();
- this.parts.add(p);
- return p;
- }
-
- private ToStringBuilder addPart(final Object value) {
- final Part p = this.addPart();
- p.value = value;
- return this;
- }
-
- private ToStringBuilder addPart(final String fieldName, final Object value) {
- final Part p = this.addPart();
- p.fieldName = fieldName;
- p.value = value;
- return this;
- }
-
- /**
- * @return the String representation of the processed object
- */
- @Override
- public String toString() {
- boolean startProcessing = ToStringBuilder.toStringContext.startProcessing(this.instance);
- if (!startProcessing) {
- return this.toSimpleReferenceString(this.instance);
- }
- try {
- final IndentationAwareStringBuilder builder = new IndentationAwareStringBuilder();
- builder.append(typeName).append(" ");
- builder.append("[");
- String nextSeparator = "";
- if (multiLine) {
- builder.increaseIndent();
- }
- for (Part part : parts) {
- if (!skipNulls || part.value != null) {
- if (multiLine) {
- builder.newLine();
- } else {
- builder.append(nextSeparator);
- nextSeparator = ", ";
- }
- if (part.fieldName != null && this.showFieldNames) {
- builder.append(part.fieldName).append(" = ");
- }
- this.internalToString(part.value, builder);
- }
- }
- if (multiLine) {
- builder.decreaseIndent().newLine();
- }
- builder.append("]");
- return builder.toString();
- } finally {
- ToStringBuilder.toStringContext.endProcessing(this.instance);
- }
- }
-
- private void internalToString(final Object object, final IndentationAwareStringBuilder sb) {
- if (prettyPrint) {
- if (object instanceof Iterable>) {
- serializeIterable((Iterable>)object, sb);
- } else if (object instanceof Object[]) {
- sb.append(Arrays.toString((Object[])object));
- } else if (object instanceof byte[]) {
- sb.append(Arrays.toString((byte[])object));
- } else if (object instanceof char[]) {
- sb.append(Arrays.toString((char[])object));
- } else if (object instanceof int[]) {
- sb.append(Arrays.toString((int[])object));
- } else if (object instanceof boolean[]) {
- sb.append(Arrays.toString((boolean[])object));
- } else if (object instanceof long[]) {
- sb.append(Arrays.toString((long[])object));
- } else if (object instanceof float[]) {
- sb.append(Arrays.toString((float[])object));
- } else if (object instanceof double[]) {
- sb.append(Arrays.toString((double[])object));
- } else if (object instanceof CharSequence) {
- sb.append("\"").append(((CharSequence)object).toString().replace("\n", "\\n").replace("\r", "\\r")).append("\"");
- } else if (object instanceof Enum>) {
- sb.append(((Enum>)object).name());
- } else {
- sb.append(String.valueOf(object));
- }
- } else {
- sb.append(String.valueOf(object));
- }
- }
-
- private void serializeIterable(final Iterable> object, final IndentationAwareStringBuilder sb) {
- final Iterator> iterator = object.iterator();
- sb.append(object.getClass().getSimpleName()).append(" (");
- if (multiLine) {
- sb.increaseIndent();
- }
- boolean wasEmpty = true;
- while (iterator.hasNext()) {
- wasEmpty = false;
- if (multiLine) {
- sb.newLine();
- }
- this.internalToString(iterator.next(), sb);
- if (iterator.hasNext()) {
- sb.append(",");
- }
- }
- if (multiLine) {
- sb.decreaseIndent();
- }
- if (!wasEmpty && this.multiLine) {
- sb.newLine();
- }
- sb.append(")");
- }
-
- private String toSimpleReferenceString(final Object obj) {
- String simpleName = obj.getClass().getSimpleName();
- int identityHashCode = System.identityHashCode(obj);
- return simpleName + "@" + Integer.valueOf(identityHashCode);
- }
-
- private List getAllDeclaredFields(final Class> clazz) {
- final ArrayList result = new ArrayList<>();
-
- for(Class> current = clazz; current != null; current = current.getSuperclass()) {
- Field[] declaredFields = current.getDeclaredFields();
- result.addAll(Arrays.asList(declaredFields));
-
- }
- return result;
- }
-
- private static final class Part {
- private String fieldName;
- private Object value;
- }
-
- private static class IndentationAwareStringBuilder {
- private final StringBuilder builder = new StringBuilder();
-
- private final String indentationString = " ";
-
- private final String newLineString = "\n";
-
- private int indentation = 0;
-
- public IndentationAwareStringBuilder increaseIndent() {
- indentation++;
- return this;
- }
-
- public IndentationAwareStringBuilder decreaseIndent() {
- indentation--;
- return this;
- }
-
- public IndentationAwareStringBuilder append(final CharSequence string) {
- if (indentation > 0) {
- String indented = string.toString().replace(
- newLineString,
- newLineString + repeat(indentationString, indentation)
- );
- builder.append(indented);
- } else {
- builder.append(string);
- }
- return this;
- }
-
- public IndentationAwareStringBuilder newLine() {
- builder.append(newLineString).
- append(repeat(this.indentationString, this.indentation));
- return this;
- }
-
- @Override
- public String toString() {
- return this.builder.toString();
- }
-
- private String repeat(String string, int count) {
- StringBuilder result = new StringBuilder();
- for(int i=0; i < count; i++) {
- result.append(string);
- }
- return result.toString();
- }
- }
-}
\ No newline at end of file