com.teamscale.commons.lang.ToStringHelpers Maven / Gradle / Ivy
Show all versions of teamscale-commons Show documentation
/*
* Copyright (c) CQSE GmbH
*
* Licensed 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 com.teamscale.commons.lang;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.collect.Iterables;
/**
* Utilities for working with Guava's {@link ToStringHelper}.
*/
public class ToStringHelpers {
/**
* A reflective variant of {@link MoreObjects#toStringHelper(Object)}.
*
* It is significantly slower than its {@code MoreObjects} counterpart, so it should not be used in
* performance-critical code.
*
* @return a {@link ToStringHelper} with all fields of {@literal self} already
* {@linkplain ToStringHelper#add(String, Object) added}.
*/
public static ToStringHelper toReflectiveStringHelper(Object self) {
if (self == null) {
return MoreObjects.toStringHelper("null");
}
ToStringHelper helper = MoreObjects.toStringHelper(self);
Class> clazz = self.getClass();
if (clazz.isArray()) {
return addArrayElements(helper, self);
}
if (Iterable.class.isAssignableFrom(clazz)) {
Object[] array = Iterables.toArray((Iterable) self, Object.class);
return addArrayElements(helper, array);
}
return addInstanceFields(helper, self);
}
private static ToStringHelper addArrayElements(ToStringHelper helper, Object array) {
int length = Array.getLength(array);
for (int i = 0; i < length; i++) {
Object element = Array.get(array, i);
helper.addValue(element);
}
return helper;
}
private static ToStringHelper addInstanceFields(ToStringHelper helper, Object instance) {
List allDeclaredFields = getAllProperInstanceFields(instance);
allDeclaredFields.sort(Comparator.comparing(Field::getName));
for (Field field : allDeclaredFields) {
try {
field.setAccessible(true);
String name = field.getName();
Object value = field.get(instance);
helper.add(name, value);
} catch (IllegalArgumentException | IllegalAccessException e) {
// Ignore and proceed on a best-effort basis
continue;
}
}
return helper;
}
/**
* @return all proper instance (i.e., not {@code static}) fields of the given object. Fields that
* are only synthesized by the compiler are omitted.
*/
private static List getAllProperInstanceFields(Object instance) {
List declaredFields = new ArrayList<>();
Class extends Object> clazz = instance.getClass();
do {
for (Field field : clazz.getDeclaredFields()) {
if (field.isSynthetic()) {
continue;
}
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
declaredFields.add(field);
}
clazz = clazz.getSuperclass();
} while (clazz != null);
return declaredFields;
}
}