diff --git a/CHANGELOG.md b/CHANGELOG.md index f4ed8fb1..6b68c3a0 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. + * 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