/*
* 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 org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.jetbrains.annotations.Nullable;
import java.io.Externalizable;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.EventListener;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE;
/**
* Provides auto-generation framework for {@code toString()} output.
* <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 Map<String, GridToStringClassDescriptor> classCache = new HashMap<>();
/** */
private static final ReadWriteLock rwLock = new ReentrantReadWriteLock();
/** Maximum number of collection (map) entries to print. */
public static final int MAX_COL_SIZE = 100;
/** {@link IgniteSystemProperties#IGNITE_TO_STRING_INCLUDE_SENSITIVE} */
public static final boolean INCLUDE_SENSITIVE =
IgniteSystemProperties.getBoolean(IGNITE_TO_STRING_INCLUDE_SENSITIVE, true);
/** */
private static ThreadLocal<Queue<GridToStringThreadLocal>> threadCache = new ThreadLocal<Queue<GridToStringThreadLocal>>() {
@Override protected Queue<GridToStringThreadLocal> initialValue() {
Queue<GridToStringThreadLocal> queue = new LinkedList<>();
queue.offer(new GridToStringThreadLocal());
return queue;
}
};
/**
* 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.
* @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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] addNames = tmp.getAdditionalNames();
Object[] addVals = tmp.getAdditionalValues();
boolean[] addSens = tmp.getAdditionalSensitives();
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;
try {
return toStringImpl(cls, tmp.getStringBuilder(), obj, addNames, addVals, addSens, 5);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] addNames = tmp.getAdditionalNames();
Object[] addVals = tmp.getAdditionalValues();
boolean[] addSens = tmp.getAdditionalSensitives();
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;
try {
return toStringImpl(cls, tmp.getStringBuilder(), obj, addNames, addVals, addSens, 4);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] addNames = tmp.getAdditionalNames();
Object[] addVals = tmp.getAdditionalValues();
boolean[] addSens = tmp.getAdditionalSensitives();
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;
try {
return toStringImpl(cls, tmp.getStringBuilder(), obj, addNames, addVals, addSens, 3);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] addNames = tmp.getAdditionalNames();
Object[] addVals = tmp.getAdditionalValues();
boolean[] addSens = tmp.getAdditionalSensitives();
addNames[0] = name0;
addVals[0] = val0;
addSens[0] = sens0;
addNames[1] = name1;
addVals[1] = val1;
addSens[1] = sens1;
try {
return toStringImpl(cls, tmp.getStringBuilder(), obj, addNames, addVals, addSens, 2);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] addNames = tmp.getAdditionalNames();
Object[] addVals = tmp.getAdditionalValues();
boolean[] addSens = tmp.getAdditionalSensitives();
addNames[0] = name;
addVals[0] = val;
addSens[0] = sens;
try {
return toStringImpl(cls, tmp.getStringBuilder(), obj, addNames, addVals, addSens, 1);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
try {
return toStringImpl(cls, tmp.getStringBuilder(), obj, tmp.getAdditionalNames(),
tmp.getAdditionalValues(), null, 0);
}
finally {
queue.offer(tmp);
}
}
/**
* 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);
}
/**
* 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.
*/
@SuppressWarnings({"unchecked"})
private static <T> String toStringImpl(Class<T> cls, SB 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;
try {
GridToStringClassDescriptor cd = getClassDescriptor(cls);
assert cd != null;
buf.setLength(0);
buf.a(cd.getSimpleClassName()).a(" [");
boolean first = true;
for (GridToStringFieldDescriptor fd : cd.getFields()) {
if (!first)
buf.a(", ");
else
first = false;
String name = fd.getName();
Field field = cls.getDeclaredField(name);
field.setAccessible(true);
buf.a(name).a('=');
Class<?> fieldType = field.getType();
if (fieldType.isArray())
buf.a(arrayToString(fieldType, field.get(obj)));
else {
Object val = field.get(obj);
if (val instanceof Collection && ((Collection)val).size() > MAX_COL_SIZE)
val = F.retain((Collection)val, true, MAX_COL_SIZE);
else if (val instanceof Map && ((Map)val).size() > MAX_COL_SIZE) {
Map tmp = U.newHashMap(MAX_COL_SIZE);
int cntr = 0;
for (Object o : ((Map)val).entrySet()) {
Map.Entry e = (Map.Entry)o;
tmp.put(e.getKey(), e.getValue());
if (++cntr >= MAX_COL_SIZE)
break;
}
val = tmp;
}
buf.a(val);
}
}
appendVals(buf, first, addNames, addVals, addSens, addLen);
buf.a(']');
return buf.toString();
}
// Specifically catching all exceptions.
catch (Exception e) {
rwLock.writeLock().lock();
// Remove entry from cache to avoid potential memory leak
// in case new class loader got loaded under the same identity hash.
try {
classCache.remove(cls.getName() + System.identityHashCode(cls.getClassLoader()));
}
finally {
rwLock.writeLock().unlock();
}
// No other option here.
throw new IgniteException(e);
}
}
/**
* @param arrType Type of the array.
* @param arr Array object.
* @return String representation of an array.
*/
public static String arrayToString(Class arrType, Object arr) {
if (arrType.equals(byte[].class))
return Arrays.toString((byte[])arr);
if (arrType.equals(boolean[].class))
return Arrays.toString((boolean[])arr);
if (arrType.equals(short[].class))
return Arrays.toString((short[])arr);
if (arrType.equals(int[].class))
return Arrays.toString((int[])arr);
if (arrType.equals(long[].class))
return Arrays.toString((long[])arr);
if (arrType.equals(float[].class))
return Arrays.toString((float[])arr);
if (arrType.equals(double[].class))
return Arrays.toString((double[])arr);
if (arrType.equals(char[].class))
return Arrays.toString((char[])arr);
return Arrays.toString((Object[])arr);
}
/**
* 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);
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] propNames = tmp.getAdditionalNames();
Object[] propVals = tmp.getAdditionalValues();
boolean[] propSens = tmp.getAdditionalSensitives();
propNames[0] = name;
propVals[0] = val;
propSens[0] = sens;
try {
return toStringImpl(str, tmp.getStringBuilder(), propNames, propVals, propSens, 1);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] propNames = tmp.getAdditionalNames();
Object[] propVals = tmp.getAdditionalValues();
boolean[] propSens = tmp.getAdditionalSensitives();
propNames[0] = name0;
propVals[0] = val0;
propSens[0] = sens0;
propNames[1] = name1;
propVals[1] = val1;
propSens[1] = sens1;
try {
return toStringImpl(str, tmp.getStringBuilder(), propNames, propVals, propSens, 2);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] propNames = tmp.getAdditionalNames();
Object[] propVals = tmp.getAdditionalValues();
boolean[] propSens = tmp.getAdditionalSensitives();
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;
try {
return toStringImpl(str, tmp.getStringBuilder(), propNames, propVals, propSens, 3);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] propNames = tmp.getAdditionalNames();
Object[] propVals = tmp.getAdditionalValues();
boolean[] propSens = tmp.getAdditionalSensitives();
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;
try {
return toStringImpl(str, tmp.getStringBuilder(), propNames, propVals, propSens, 4);
}
finally {
queue.offer(tmp);
}
}
/**
* 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;
Queue<GridToStringThreadLocal> queue = threadCache.get();
assert queue != null;
// Since string() methods can be chain-called from the same thread we
// have to keep a list of thread-local objects and remove/add them
// in each string() apply.
GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
Object[] propNames = tmp.getAdditionalNames();
Object[] propVals = tmp.getAdditionalValues();
boolean[] propSens = tmp.getAdditionalSensitives();
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;
try {
return toStringImpl(str, tmp.getStringBuilder(), propNames, propVals, propSens, 5);
}
finally {
queue.offer(tmp);
}
}
/**
* 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, SB buf, Object[] propNames, Object[] propVals,
boolean[] propSens, int propCnt) {
buf.setLength(0);
if (str != null)
buf.a(str).a(" ");
buf.a("[");
appendVals(buf, true, propNames, propVals, propSens, propCnt);
buf.a(']');
return buf.toString();
}
/**
* 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(SB 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] && !INCLUDE_SENSITIVE)
continue;
GridToStringInclude incAnn = addVal.getClass().getAnnotation(GridToStringInclude.class);
if (incAnn != null && incAnn.sensitive() && !INCLUDE_SENSITIVE)
continue;
Class<?> cls = addVal.getClass();
if (cls.isArray())
addVal = arrayToString(cls, addVal);
}
if (!first)
buf.a(", ");
else
first = false;
buf.a(addNames[i]).a('=').a(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;
rwLock.readLock().lock();
try {
cd = classCache.get(key);
}
finally {
rwLock.readLock().unlock();
}
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 || INCLUDE_SENSITIVE;
}
else if (!f.isAnnotationPresent(GridToStringExclude.class) &&
!f.getType().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.getName());
// Get order, if any.
final GridToStringOrder annOrder = f.getAnnotation(GridToStringOrder.class);
if (annOrder != null)
fd.setOrder(annOrder.value());
cd.addField(fd);
}
}
cd.sortFields();
/*
* Allow multiple puts for the same class - they will simply override.
*/
rwLock.writeLock().lock();
try {
classCache.put(key, cd);
}
finally {
rwLock.writeLock().unlock();
}
}
return cd;
}
}