blob: 1263076dd963a84e2644cacc01ad0ebb981722ae [file] [log] [blame]
/*
* 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.ignite.internal.util.tostring;
import java.io.Externalizable;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static java.util.Objects.nonNull;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_COLLECTION_LIMIT;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE;
import static org.apache.ignite.IgniteSystemProperties.getBoolean;
/**
* Provides auto-generation framework for {@code toString()} output.
* <p>
* In case of recursion, repeatable objects will be shown as "ClassName@hash".
* But fields will be printed only for the first entry to prevent recursion.
* <p>
* Default exclusion policy (can be overridden with {@link GridToStringInclude}
* annotation):
* <ul>
* <li>fields with {@link GridToStringExclude} annotations
* <li>classes that have {@link GridToStringExclude} annotation (current list):
* <ul>
* <li>GridManager
* <li>GridManagerRegistry
* <li>GridProcessor
* <li>GridProcessorRegistry
* <li>IgniteLogger
* <li>GridDiscoveryMetricsProvider
* <li>GridByteArrayList
* </ul>
* <li>static fields
* <li>non-private fields
* <li>arrays
* <li>fields of type {@link Object}
* <li>fields of type {@link Thread}
* <li>fields of type {@link Runnable}
* <li>fields of type {@link Serializable}
* <li>fields of type {@link Externalizable}
* <li>{@link InputStream} implementations
* <li>{@link OutputStream} implementations
* <li>{@link EventListener} implementations
* <li>{@link Lock} implementations
* <li>{@link ReadWriteLock} implementations
* <li>{@link Condition} implementations
* <li>{@link Map} implementations
* <li>{@link Collection} implementations
* </ul>
*/
public class GridToStringBuilder {
/** */
private static final Object[] EMPTY_ARRAY = new Object[0];
/** */
private static final Map<String, GridToStringClassDescriptor> classCache = new ConcurrentHashMap<>();
/** Supplier for {@link #includeSensitive} with default behavior. */
private static final AtomicReference<Supplier<Boolean>> INCL_SENS_SUP_REF =
new AtomicReference<>(new Supplier<Boolean>() {
/** Value of "IGNITE_TO_STRING_INCLUDE_SENSITIVE". */
final boolean INCLUDE_SENSITIVE = getBoolean(IGNITE_TO_STRING_INCLUDE_SENSITIVE, true);
/** {@inheritDoc} */
@Override public Boolean get() {
return INCLUDE_SENSITIVE;
}
});
/** */
private static final int COLLECTION_LIMIT =
IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
/** Every thread has its own string builder. */
private static ThreadLocal<SBLimitedLength> threadLocSB = new ThreadLocal<SBLimitedLength>() {
@Override protected SBLimitedLength initialValue() {
SBLimitedLength sb = new SBLimitedLength(256);
sb.initLimit(new SBLengthLimit());
return sb;
}
};
/**
* Contains objects currently printing in the string builder.
* <p>
* Since {@code toString()} methods can be chain-called from the same thread we
* have to keep a map of this objects pointed to the position of previous occurrence
* and remove/add them in each {@code toString()} apply.
*/
private static ThreadLocal<IdentityHashMap<Object, EntryReference>> savedObjects = new ThreadLocal<IdentityHashMap<Object, EntryReference>>() {
@Override protected IdentityHashMap<Object, EntryReference> initialValue() {
return new IdentityHashMap<>();
}
};
/**
* Implementation of the <a href=
* "https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom">
* "Initialization-on-demand holder idiom"</a>.
*/
private static class Holder {
/** Supplier holder for {@link #includeSensitive}. */
static final Supplier<Boolean> INCL_SENS_SUP = INCL_SENS_SUP_REF.get();
}
/**
* Setting the logic of the {@link #includeSensitive} method. <br/>
* By default, it take the value of
* {@link IgniteSystemProperties#IGNITE_TO_STRING_INCLUDE_SENSITIVE
* IGNITE_TO_STRING_INCLUDE_SENSITIVE} system property. <br/>
* <b>Important!</b> Changing the logic is possible only until the first
* call of {@link #includeSensitive} method. <br/>
*
* @param sup
*/
public static void setIncludeSensitiveSupplier(Supplier<Boolean> sup) {
assert nonNull(sup);
INCL_SENS_SUP_REF.set(sup);
}
/**
* Return {@code true} if need to include sensitive data otherwise
* {@code false}.
*
* @return {@code true} if need to include sensitive data otherwise
* {@code false}.
* @see GridToStringBuilder#setIncludeSensitiveSupplier(Supplier)
*/
public static boolean includeSensitive() {
return Holder.INCL_SENS_SUP.get();
}
/**
* @param obj Object.
* @return Hexed identity hashcode.
*/
public static String identity(Object obj) {
return '@' + Integer.toHexString(System.identityHashCode(obj));
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param name4 Additional parameter name.
* @param val4 Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0,
String name1, Object val1,
String name2, Object val2,
String name3, Object val3,
String name4, Object val4) {
return toString(cls,
obj,
name0, val0, false,
name1, val1, false,
name2, val2, false,
name3, val3, false,
name4, val4, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param name4 Additional parameter name.
* @param val4 Additional parameter value.
* @param name5 Additional parameter name.
* @param val5 Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0,
String name1, Object val1,
String name2, Object val2,
String name3, Object val3,
String name4, Object val4,
String name5, Object val5) {
return toString(cls,
obj,
name0, val0, false,
name1, val1, false,
name2, val2, false,
name3, val3, false,
name4, val4, false,
name5, val5, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param name4 Additional parameter name.
* @param val4 Additional parameter value.
* @param name5 Additional parameter name.
* @param val5 Additional parameter value.
* @param name6 Additional parameter name.
* @param val6 Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0,
String name1, Object val1,
String name2, Object val2,
String name3, Object val3,
String name4, Object val4,
String name5, Object val5,
String name6, Object val6) {
return toString(cls,
obj,
name0, val0, false,
name1, val1, false,
name2, val2, false,
name3, val3, false,
name4, val4, false,
name5, val5, false,
name6, val6, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param sens0 Property sensitive flag.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param sens1 Property sensitive flag.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param sens2 Property sensitive flag.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param sens3 Property sensitive flag.
* @param name4 Additional parameter name.
* @param val4 Additional parameter value.
* @param sens4 Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0, boolean sens0,
String name1, Object val1, boolean sens1,
String name2, Object val2, boolean sens2,
String name3, Object val3, boolean sens3,
String name4, Object val4, boolean sens4) {
assert cls != null;
assert obj != null;
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
assert name4 != null;
Object[] addNames = new Object[5];
Object[] addVals = new Object[5];
boolean[] addSens = new boolean[5];
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
addNames[2] = name2;
addVals[2] = val2;
addSens[2] = sens2;
addNames[3] = name3;
addVals[3] = val3;
addSens[3] = sens3;
addNames[4] = name4;
addVals[4] = val4;
addSens[4] = sens4;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 5);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param sens0 Property sensitive flag.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param sens1 Property sensitive flag.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param sens2 Property sensitive flag.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param sens3 Property sensitive flag.
* @param name4 Additional parameter name.
* @param val4 Additional parameter value.
* @param sens4 Property sensitive flag.
* @param name5 Additional parameter name.
* @param val5 Additional parameter value.
* @param sens5 Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0, boolean sens0,
String name1, Object val1, boolean sens1,
String name2, Object val2, boolean sens2,
String name3, Object val3, boolean sens3,
String name4, Object val4, boolean sens4,
String name5, Object val5, boolean sens5) {
assert cls != null;
assert obj != null;
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
assert name4 != null;
assert name5 != null;
Object[] addNames = new Object[6];
Object[] addVals = new Object[6];
boolean[] addSens = new boolean[6];
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
addNames[2] = name2;
addVals[2] = val2;
addSens[2] = sens2;
addNames[3] = name3;
addVals[3] = val3;
addSens[3] = sens3;
addNames[4] = name4;
addVals[4] = val4;
addSens[4] = sens4;
addNames[5] = name5;
addVals[5] = val5;
addSens[5] = sens5;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 6);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param sens0 Property sensitive flag.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param sens1 Property sensitive flag.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param sens2 Property sensitive flag.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param sens3 Property sensitive flag.
* @param name4 Additional parameter name.
* @param val4 Additional parameter value.
* @param sens4 Property sensitive flag.
* @param name5 Additional parameter name.
* @param val5 Additional parameter value.
* @param sens5 Property sensitive flag.
* @param name6 Additional parameter name.
* @param val6 Additional parameter value.
* @param sens6 Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0, boolean sens0,
String name1, Object val1, boolean sens1,
String name2, Object val2, boolean sens2,
String name3, Object val3, boolean sens3,
String name4, Object val4, boolean sens4,
String name5, Object val5, boolean sens5,
String name6, Object val6, boolean sens6) {
assert cls != null;
assert obj != null;
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
assert name4 != null;
assert name5 != null;
assert name6 != null;
Object[] addNames = new Object[7];
Object[] addVals = new Object[7];
boolean[] addSens = new boolean[7];
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
addNames[2] = name2;
addVals[2] = val2;
addSens[2] = sens2;
addNames[3] = name3;
addVals[3] = val3;
addSens[3] = sens3;
addNames[4] = name4;
addVals[4] = val4;
addSens[4] = sens4;
addNames[5] = name5;
addVals[5] = val5;
addSens[5] = sens5;
addNames[6] = name6;
addVals[6] = val6;
addSens[6] = sens6;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 7);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0,
String name1, Object val1,
String name2, Object val2,
String name3, Object val3) {
return toString(cls, obj,
name0, val0, false,
name1, val1, false,
name2, val2, false,
name3, val3, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param sens0 Property sensitive flag.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param sens1 Property sensitive flag.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param sens2 Property sensitive flag.
* @param name3 Additional parameter name.
* @param val3 Additional parameter value.
* @param sens3 Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0, boolean sens0,
String name1, Object val1, boolean sens1,
String name2, Object val2, boolean sens2,
String name3, Object val3, boolean sens3) {
assert cls != null;
assert obj != null;
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
Object[] addNames = new Object[4];
Object[] addVals = new Object[4];
boolean[] addSens = new boolean[4];
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
addNames[2] = name2;
addVals[2] = val2;
addSens[2] = sens2;
addNames[3] = name3;
addVals[3] = val3;
addSens[3] = sens3;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 4);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0,
String name1, Object val1,
String name2, Object val2) {
return toString(cls,
obj,
name0, val0, false,
name1, val1, false,
name2, val2, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param sens0 Property sensitive flag.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param sens1 Property sensitive flag.
* @param name2 Additional parameter name.
* @param val2 Additional parameter value.
* @param sens2 Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0, boolean sens0,
String name1, Object val1, boolean sens1,
String name2, Object val2, boolean sens2) {
assert cls != null;
assert obj != null;
assert name0 != null;
assert name1 != null;
assert name2 != null;
Object[] addNames = new Object[3];
Object[] addVals = new Object[3];
boolean[] addSens = new boolean[3];
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
addNames[2] = name2;
addVals[2] = val2;
addSens[2] = sens2;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 3);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0,
String name1, Object val1) {
return toString(cls, obj, name0, val0, false, name1, val1, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name0 Additional parameter name.
* @param val0 Additional parameter value.
* @param sens0 Property sensitive flag.
* @param name1 Additional parameter name.
* @param val1 Additional parameter value.
* @param sens1 Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj,
String name0, Object val0, boolean sens0,
String name1, Object val1, boolean sens1) {
assert cls != null;
assert obj != null;
assert name0 != null;
assert name1 != null;
Object[] addNames = new Object[2];
Object[] addVals = new Object[2];
boolean[] addSens = new boolean[2];
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 2);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name Additional parameter name.
* @param val Additional parameter value.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj, String name, @Nullable Object val) {
return toString(cls, obj, name, val, false);
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param name Additional parameter name.
* @param val Additional parameter value.
* @param sens Property sensitive flag.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj, String name, @Nullable Object val, boolean sens) {
assert cls != null;
assert obj != null;
assert name != null;
Object[] addNames = new Object[1];
Object[] addVals = new Object[1];
boolean[] addSens = new boolean[1];
addNames[0] = name;
addVals[0] = val;
addSens[0] = sens;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 1);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj) {
assert cls != null;
assert obj != null;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(cls, sb, obj, EMPTY_ARRAY, EMPTY_ARRAY, null, 0);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces auto-generated output of string presentation for given object and its declaration class.
*
* @param <T> Type of the object.
* @param cls Declaration class of the object. Note that this should not be a runtime class.
* @param obj Object to get a string presentation for.
* @param parent String representation of parent.
* @return String presentation of the given object.
*/
public static <T> String toString(Class<T> cls, T obj, String parent) {
return parent != null ? toString(cls, obj, "super", parent) : toString(cls, obj);
}
/**
* Print value with length limitation.
*
* @param buf buffer to print to.
* @param val value to print, can be {@code null}.
*/
private static void toString(SBLimitedLength buf, Object val) {
toString(buf, null, val);
}
/**
* Print value with length limitation.
*
* @param buf buffer to print to.
* @param cls value class.
* @param val value to print.
*/
private static void toString(SBLimitedLength buf, Class<?> cls, Object val) {
if (val == null) {
buf.a("null");
return;
}
if (cls == null)
cls = val.getClass();
if (cls.isPrimitive()) {
buf.a(val);
return;
}
IdentityHashMap<Object, EntryReference> svdObjs = savedObjects.get();
if (handleRecursion(buf, val, cls, svdObjs))
return;
svdObjs.put(val, new EntryReference(buf.length()));
try {
if (cls.isArray())
addArray(buf, cls, val);
else if (val instanceof Collection)
addCollection(buf, (Collection) val);
else if (val instanceof Map)
addMap(buf, (Map<?, ?>) val);
else
buf.a(val);
}
finally {
svdObjs.remove(val);
}
}
/**
* Writes array to buffer.
*
* @param buf String builder buffer.
* @param arrType Type of the array.
* @param obj Array object.
*/
private static void addArray(SBLimitedLength buf, Class arrType, Object obj) {
if (arrType.getComponentType().isPrimitive()) {
buf.a(arrayToString(obj));
return;
}
Object[] arr = (Object[]) obj;
buf.a(arrType.getSimpleName()).a(" [");
for (int i = 0; i < arr.length; i++) {
toString(buf, arr[i]);
if (i == COLLECTION_LIMIT - 1 || i == arr.length - 1)
break;
buf.a(", ");
}
handleOverflow(buf, arr.length);
buf.a(']');
}
/**
* Writes collection to buffer.
*
* @param buf String builder buffer.
* @param col Collection object.
*/
private static void addCollection(SBLimitedLength buf, Collection col) {
buf.a(col.getClass().getSimpleName()).a(" [");
int cnt = 0;
for (Object obj : col) {
toString(buf, obj);
if (++cnt == COLLECTION_LIMIT || cnt == col.size())
break;
buf.a(", ");
}
handleOverflow(buf, col.size());
buf.a(']');
}
/**
* Writes map to buffer.
*
* @param buf String builder buffer.
* @param map Map object.
*/
private static <K, V> void addMap(SBLimitedLength buf, Map<K, V> map) {
buf.a(map.getClass().getSimpleName()).a(" {");
int cnt = 0;
for (Map.Entry<K, V> e : map.entrySet()) {
toString(buf, e.getKey());
buf.a('=');
toString(buf, e.getValue());
if (++cnt == COLLECTION_LIMIT || cnt == map.size())
break;
buf.a(", ");
}
handleOverflow(buf, map.size());
buf.a('}');
}
/**
* Writes overflow message to buffer if needed.
*
* @param buf String builder buffer.
* @param size Size to compare with limit.
*/
private static void handleOverflow(SBLimitedLength buf, int size) {
int overflow = size - COLLECTION_LIMIT;
if (overflow > 0)
buf.a("... and ").a(overflow).a(" more");
}
/**
* Creates an uniformed string presentation for the given object.
*
* @param <T> Type of object.
* @param cls Class of the object.
* @param buf String builder buffer.
* @param obj Object for which to get string presentation.
* @param addNames Names of additional values to be included.
* @param addVals Additional values to be included.
* @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
* @param addLen How many additional values will be included.
* @return String presentation of the given object.
*/
private static <T> String toStringImpl(
Class<T> cls,
SBLimitedLength buf,
T obj,
Object[] addNames,
Object[] addVals,
@Nullable boolean[] addSens,
int addLen) {
assert cls != null;
assert buf != null;
assert obj != null;
assert addNames != null;
assert addVals != null;
assert addNames.length == addVals.length;
assert addLen <= addNames.length;
boolean newStr = buf.length() == 0;
IdentityHashMap<Object, EntryReference> svdObjs = savedObjects.get();
if (newStr)
svdObjs.put(obj, new EntryReference(buf.length()));
try {
int len = buf.length();
String s = toStringImpl0(cls, buf, obj, addNames, addVals, addSens, addLen);
if (newStr)
return s;
buf.setLength(len);
return s.substring(len);
}
finally {
if (newStr)
svdObjs.remove(obj);
}
}
/**
* Creates an uniformed string presentation for the given object.
*
* @param cls Class of the object.
* @param buf String builder buffer.
* @param obj Object for which to get string presentation.
* @param addNames Names of additional values to be included.
* @param addVals Additional values to be included.
* @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
* @param addLen How many additional values will be included.
* @return String presentation of the given object.
* @param <T> Type of object.
*/
private static <T> String toStringImpl0(
Class<T> cls,
SBLimitedLength buf,
T obj,
Object[] addNames,
Object[] addVals,
@Nullable boolean[] addSens,
int addLen
) {
try {
GridToStringClassDescriptor cd = getClassDescriptor(cls);
assert cd != null;
buf.a(cd.getSimpleClassName());
EntryReference ref = savedObjects.get().get(obj);
if (ref != null && ref.hashNeeded) {
buf.a(identity(obj));
ref.hashNeeded = false;
}
buf.a(" [");
boolean first = true;
for (GridToStringFieldDescriptor fd : cd.getFields()) {
if (!first)
buf.a(", ");
else
first = false;
buf.a(fd.getName()).a('=');
switch (fd.type()) {
case GridToStringFieldDescriptor.FIELD_TYPE_OBJECT:
toString(buf, fd.fieldClass(), GridUnsafe.getObjectField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_BYTE:
buf.a(GridUnsafe.getByteField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_BOOLEAN:
buf.a(GridUnsafe.getBooleanField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_CHAR:
buf.a(GridUnsafe.getCharField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_SHORT:
buf.a(GridUnsafe.getShortField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_INT:
buf.a(GridUnsafe.getIntField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_FLOAT:
buf.a(GridUnsafe.getFloatField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_LONG:
buf.a(GridUnsafe.getLongField(obj, fd.offset()));
break;
case GridToStringFieldDescriptor.FIELD_TYPE_DOUBLE:
buf.a(GridUnsafe.getDoubleField(obj, fd.offset()));
break;
}
}
appendVals(buf, first, addNames, addVals, addSens, addLen);
buf.a(']');
return buf.toString();
}
// Specifically catching all exceptions.
catch (Exception e) {
// Remove entry from cache to avoid potential memory leak
// in case new class loader got loaded under the same identity hash.
classCache.remove(cls.getName() + System.identityHashCode(cls.getClassLoader()));
// No other option here.
throw new IgniteException(e);
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name Property name.
* @param val Property value.
* @return String presentation.
*/
public static String toString(String str, String name, @Nullable Object val) {
return toString(str, name, val, false);
}
/**
* Returns limited string representation of array.
*
* @param arr Array object. Each value is automatically wrapped if it has a primitive type.
* @return String representation of an array.
*/
public static String arrayToString(Object arr) {
if (arr == null)
return "null";
String res;
int arrLen;
if (arr instanceof Object[]) {
Object[] objArr = (Object[])arr;
arrLen = objArr.length;
if (arrLen > COLLECTION_LIMIT)
objArr = Arrays.copyOf(objArr, COLLECTION_LIMIT);
res = Arrays.toString(objArr);
} else {
res = toStringWithLimit(arr, COLLECTION_LIMIT);
arrLen = Array.getLength(arr);
}
if (arrLen > COLLECTION_LIMIT) {
StringBuilder resSB = new StringBuilder(res);
resSB.deleteCharAt(resSB.length() - 1);
resSB.append("... and ").append(arrLen - COLLECTION_LIMIT).append(" more]");
res = resSB.toString();
}
return res;
}
/**
* Returns limited string representation of array.
*
* @param arr Input array. Each value is automatically wrapped if it has a primitive type.
* @param limit max array items to string limit.
* @return String representation of an array.
*/
private static String toStringWithLimit(Object arr, int limit) {
int arrIdxMax = Array.getLength(arr) - 1;
if (arrIdxMax == -1)
return "[]";
int idxMax = Math.min(arrIdxMax, limit);
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; i <= idxMax; ++i) {
b.append(Array.get(arr, i));
if (i == idxMax)
return b.append(']').toString();
b.append(", ");
}
return b.toString();
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name Property name.
* @param val Property value.
* @param sens Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str, String name, @Nullable Object val, boolean sens) {
assert name != null;
Object[] propNames = new Object[1];
Object[] propVals = new Object[1];
boolean[] propSens = new boolean[1];
propNames[0] = name;
propVals[0] = val;
propSens[0] = sens;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 1);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param name1 Property name.
* @param val1 Property value.
* @return String presentation.
*/
public static String toString(String str, String name0, @Nullable Object val0, String name1,
@Nullable Object val1) {
return toString(str, name0, val0, false, name1, val1, false);
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param sens0 Property sensitive flag.
* @param name1 Property name.
* @param val1 Property value.
* @param sens1 Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str,
String name0, @Nullable Object val0, boolean sens0,
String name1, @Nullable Object val1, boolean sens1) {
assert name0 != null;
assert name1 != null;
Object[] propNames = new Object[2];
Object[] propVals = new Object[2];
boolean[] propSens = new boolean[2];
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 2);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param sens0 Property sensitive flag.
* @param name1 Property name.
* @param val1 Property value.
* @param sens1 Property sensitive flag.
* @param name2 Property name.
* @param val2 Property value.
* @param sens2 Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str,
String name0, @Nullable Object val0, boolean sens0,
String name1, @Nullable Object val1, boolean sens1,
String name2, @Nullable Object val2, boolean sens2) {
assert name0 != null;
assert name1 != null;
assert name2 != null;
Object[] propNames = new Object[3];
Object[] propVals = new Object[3];
boolean[] propSens = new boolean[3];
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
propNames[2] = name2;
propVals[2] = val2;
propSens[2] = sens2;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 3);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param sens0 Property sensitive flag.
* @param name1 Property name.
* @param val1 Property value.
* @param sens1 Property sensitive flag.
* @param name2 Property name.
* @param val2 Property value.
* @param sens2 Property sensitive flag.
* @param name3 Property name.
* @param val3 Property value.
* @param sens3 Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str,
String name0, @Nullable Object val0, boolean sens0,
String name1, @Nullable Object val1, boolean sens1,
String name2, @Nullable Object val2, boolean sens2,
String name3, @Nullable Object val3, boolean sens3) {
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
Object[] propNames = new Object[4];
Object[] propVals = new Object[4];
boolean[] propSens = new boolean[4];
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
propNames[2] = name2;
propVals[2] = val2;
propSens[2] = sens2;
propNames[3] = name3;
propVals[3] = val3;
propSens[3] = sens3;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 4);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param sens0 Property sensitive flag.
* @param name1 Property name.
* @param val1 Property value.
* @param sens1 Property sensitive flag.
* @param name2 Property name.
* @param val2 Property value.
* @param sens2 Property sensitive flag.
* @param name3 Property name.
* @param val3 Property value.
* @param sens3 Property sensitive flag.
* @param name4 Property name.
* @param val4 Property value.
* @param sens4 Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str,
String name0, @Nullable Object val0, boolean sens0,
String name1, @Nullable Object val1, boolean sens1,
String name2, @Nullable Object val2, boolean sens2,
String name3, @Nullable Object val3, boolean sens3,
String name4, @Nullable Object val4, boolean sens4) {
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
assert name4 != null;
Object[] propNames = new Object[5];
Object[] propVals = new Object[5];
boolean[] propSens = new boolean[5];
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
propNames[2] = name2;
propVals[2] = val2;
propSens[2] = sens2;
propNames[3] = name3;
propVals[3] = val3;
propSens[3] = sens3;
propNames[4] = name4;
propVals[4] = val4;
propSens[4] = sens4;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 5);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param sens0 Property sensitive flag.
* @param name1 Property name.
* @param val1 Property value.
* @param sens1 Property sensitive flag.
* @param name2 Property name.
* @param val2 Property value.
* @param sens2 Property sensitive flag.
* @param name3 Property name.
* @param val3 Property value.
* @param sens3 Property sensitive flag.
* @param name4 Property name.
* @param val4 Property value.
* @param sens4 Property sensitive flag.
* @param name5 Property name.
* @param val5 Property value.
* @param sens5 Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str,
String name0, @Nullable Object val0, boolean sens0,
String name1, @Nullable Object val1, boolean sens1,
String name2, @Nullable Object val2, boolean sens2,
String name3, @Nullable Object val3, boolean sens3,
String name4, @Nullable Object val4, boolean sens4,
String name5, @Nullable Object val5, boolean sens5) {
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
assert name4 != null;
assert name5 != null;
Object[] propNames = new Object[6];
Object[] propVals = new Object[6];
boolean[] propSens = new boolean[6];
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
propNames[2] = name2;
propVals[2] = val2;
propSens[2] = sens2;
propNames[3] = name3;
propVals[3] = val3;
propSens[3] = sens3;
propNames[4] = name4;
propVals[4] = val4;
propSens[4] = sens4;
propNames[5] = name5;
propVals[5] = val5;
propSens[5] = sens5;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 6);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Produces uniformed output of string with context properties
*
* @param str Output prefix or {@code null} if empty.
* @param name0 Property name.
* @param val0 Property value.
* @param sens0 Property sensitive flag.
* @param name1 Property name.
* @param val1 Property value.
* @param sens1 Property sensitive flag.
* @param name2 Property name.
* @param val2 Property value.
* @param sens2 Property sensitive flag.
* @param name3 Property name.
* @param val3 Property value.
* @param sens3 Property sensitive flag.
* @param name4 Property name.
* @param val4 Property value.
* @param sens4 Property sensitive flag.
* @param name5 Property name.
* @param val5 Property value.
* @param sens5 Property sensitive flag.
* @param name6 Property name.
* @param val6 Property value.
* @param sens6 Property sensitive flag.
* @return String presentation.
*/
public static String toString(String str,
String name0, @Nullable Object val0, boolean sens0,
String name1, @Nullable Object val1, boolean sens1,
String name2, @Nullable Object val2, boolean sens2,
String name3, @Nullable Object val3, boolean sens3,
String name4, @Nullable Object val4, boolean sens4,
String name5, @Nullable Object val5, boolean sens5,
String name6, @Nullable Object val6, boolean sens6) {
assert name0 != null;
assert name1 != null;
assert name2 != null;
assert name3 != null;
assert name4 != null;
assert name5 != null;
assert name6 != null;
Object[] propNames = new Object[7];
Object[] propVals = new Object[7];
boolean[] propSens = new boolean[7];
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
propNames[2] = name2;
propVals[2] = val2;
propSens[2] = sens2;
propNames[3] = name3;
propVals[3] = val3;
propSens[3] = sens3;
propNames[4] = name4;
propVals[4] = val4;
propSens[4] = sens4;
propNames[5] = name5;
propVals[5] = val5;
propSens[5] = sens5;
propNames[6] = name6;
propVals[6] = val6;
propSens[6] = sens6;
SBLimitedLength sb = threadLocSB.get();
boolean newStr = sb.length() == 0;
try {
return toStringImpl(str, sb, propNames, propVals, propSens, 7);
}
finally {
if (newStr)
sb.reset();
}
}
/**
* Creates an uniformed string presentation for the binary-like object.
*
* @param str Output prefix or {@code null} if empty.
* @param buf String builder buffer.
* @param propNames Names of object properties.
* @param propVals Property values.
* @param propSens Sensitive flag of values or {@code null} if all values is not sensitive.
* @param propCnt Properties count.
* @return String presentation of the object.
*/
private static String toStringImpl(String str, SBLimitedLength buf, Object[] propNames, Object[] propVals,
boolean[] propSens, int propCnt) {
boolean newStr = buf.length() == 0;
if (str != null)
buf.a(str).a(" ");
buf.a("[");
appendVals(buf, true, propNames, propVals, propSens, propCnt);
buf.a(']');
if (newStr)
return buf.toString();
// Called from another GTSB.toString(), so this string is already in the buffer and shouldn't be returned.
return "";
}
/**
* Append additional values to the buffer.
*
* @param buf Buffer.
* @param first First value flag.
* @param addNames Names of additional values to be included.
* @param addVals Additional values to be included.
* @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
* @param addLen How many additional values will be included.
*/
private static void appendVals(SBLimitedLength buf,
boolean first,
Object[] addNames,
Object[] addVals,
boolean[] addSens,
int addLen)
{
if (addLen > 0) {
for (int i = 0; i < addLen; i++) {
Object addVal = addVals[i];
if (addVal != null) {
if (addSens != null && addSens[i] && !includeSensitive())
continue;
GridToStringInclude incAnn = addVal.getClass().getAnnotation(GridToStringInclude.class);
if (incAnn != null && incAnn.sensitive() && !includeSensitive())
continue;
}
if (!first)
buf.a(", ");
else
first = false;
buf.a(addNames[i]).a('=');
toString(buf, addVal);
}
}
}
/**
* @param cls Class.
* @param <T> Type of the object.
* @return Descriptor for the class.
*/
@SuppressWarnings({"TooBroadScope"})
private static <T> GridToStringClassDescriptor getClassDescriptor(Class<T> cls) {
assert cls != null;
String key = cls.getName() + System.identityHashCode(cls.getClassLoader());
GridToStringClassDescriptor cd;
cd = classCache.get(key);
if (cd == null) {
cd = new GridToStringClassDescriptor(cls);
for (Field f : cls.getDeclaredFields()) {
boolean add = false;
Class<?> type = f.getType();
final GridToStringInclude incFld = f.getAnnotation(GridToStringInclude.class);
final GridToStringInclude incType = type.getAnnotation(GridToStringInclude.class);
if (incFld != null || incType != null) {
// Information is not sensitive when both the field and the field type are not sensitive.
// When @GridToStringInclude is not present then the flag is false by default for that attribute.
final boolean notSens = (incFld == null || !incFld.sensitive()) && (incType == null || !incType.sensitive());
add = notSens || includeSensitive();
}
else if (!f.isAnnotationPresent(GridToStringExclude.class) &&
!type.isAnnotationPresent(GridToStringExclude.class)
) {
if (
// Include only private non-static
Modifier.isPrivate(f.getModifiers()) && !Modifier.isStatic(f.getModifiers()) &&
// No direct objects & serializable.
Object.class != type &&
Serializable.class != type &&
Externalizable.class != type &&
// No arrays.
!type.isArray() &&
// Exclude collections, IO, etc.
!EventListener.class.isAssignableFrom(type) &&
!Map.class.isAssignableFrom(type) &&
!Collection.class.isAssignableFrom(type) &&
!InputStream.class.isAssignableFrom(type) &&
!OutputStream.class.isAssignableFrom(type) &&
!Thread.class.isAssignableFrom(type) &&
!Runnable.class.isAssignableFrom(type) &&
!Lock.class.isAssignableFrom(type) &&
!ReadWriteLock.class.isAssignableFrom(type) &&
!Condition.class.isAssignableFrom(type)
)
add = true;
}
if (add) {
GridToStringFieldDescriptor fd = new GridToStringFieldDescriptor(f);
// Get order, if any.
final GridToStringOrder annOrder = f.getAnnotation(GridToStringOrder.class);
if (annOrder != null)
fd.setOrder(annOrder.value());
cd.addField(fd);
}
}
cd.sortFields();
classCache.putIfAbsent(key, cd);
}
return cd;
}
/**
* Returns sorted and compacted string representation of given {@code col}.
* Two nearby numbers with difference at most 1 are compacted to one continuous segment.
* E.g. collection of [1, 2, 3, 5, 6, 7, 10] will be compacted to [1-3, 5-7, 10].
*
* @param col Collection of integers.
* @return Compacted string representation of given collections.
*/
public static String compact(Collection<Integer> col) {
return compact(col, i -> i + 1);
}
/**
* Returns sorted and compacted string representation of given {@code col}.
* Two nearby numbers are compacted to one continuous segment.
* E.g. collection of [1, 2, 3, 5, 6, 7, 10] with
* {@code nextValFun = i -> i + 1} will be compacted to [1-3, 5-7, 10].
*
* @param col Collection of numbers.
* @param nextValFun Function to get nearby number.
* @return Compacted string representation of given collections.
*/
public static <T extends Number & Comparable<? super T>> String compact(
Collection<T> col,
Function<T, T> nextValFun
) {
assert nonNull(col);
assert nonNull(nextValFun);
if (col.isEmpty())
return "[]";
SB sb = new SB();
sb.a('[');
List<T> l = new ArrayList<>(col);
Collections.sort(l);
T left = l.get(0), right = left;
for (int i = 1; i < l.size(); i++) {
T val = l.get(i);
if (right.compareTo(val) == 0 || nextValFun.apply(right).compareTo(val) == 0) {
right = val;
continue;
}
if (left.compareTo(right) == 0)
sb.a(left);
else
sb.a(left).a('-').a(right);
sb.a(',').a(' ');
left = right = val;
}
if (left.compareTo(right) == 0)
sb.a(left);
else
sb.a(left).a('-').a(right);
sb.a(']');
return sb.toString();
}
/**
* Checks that object is already saved.
* In positive case this method inserts hash to the saved object entry (if needed) and name@hash for current entry.
* Further toString operations are not needed for current object.
*
* @param buf String builder buffer.
* @param obj Object.
* @param cls Class.
* @param svdObjs Map with saved objects to handle recursion.
* @return {@code True} if object is already saved and name@hash was added to buffer.
* {@code False} if it wasn't saved previously and it should be saved.
*/
private static boolean handleRecursion(
SBLimitedLength buf,
Object obj,
@NotNull Class cls,
IdentityHashMap<Object, EntryReference> svdObjs
) {
EntryReference ref = svdObjs.get(obj);
if (ref == null)
return false;
int pos = ref.pos;
String name = cls.getSimpleName();
String hash = identity(obj);
String savedName = name + hash;
String charsAtPos = buf.impl().substring(pos, pos + savedName.length());
if (!buf.isOverflowed() && !savedName.equals(charsAtPos)) {
if (charsAtPos.startsWith(cls.getSimpleName())) {
buf.i(pos + name.length(), hash);
incValues(svdObjs, obj, hash.length());
}
else
ref.hashNeeded = true;
}
buf.a(savedName);
return true;
}
/**
* Increment positions of already presented objects afterward given object.
*
* @param svdObjs Map with objects already presented in the buffer.
* @param obj Object.
* @param hashLen Length of the object's hash.
*/
private static void incValues(IdentityHashMap<Object, EntryReference> svdObjs, Object obj, int hashLen) {
int baseline = svdObjs.get(obj).pos;
for (IdentityHashMap.Entry<Object, EntryReference> entry : svdObjs.entrySet()) {
EntryReference ref = entry.getValue();
int pos = ref.pos;
if (pos > baseline)
ref.pos = pos + hashLen;
}
}
/**
*
*/
private static class EntryReference {
/** Position. */
int pos;
/** First object entry needs hash to be written. */
boolean hashNeeded;
/**
* @param pos Position.
*/
private EntryReference(int pos) {
this.pos = pos;
hashNeeded = false;
}
}
}