com.squeakysand.commons.lang.ToStringHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of squeakysand-commons Show documentation
Show all versions of squeakysand-commons Show documentation
Classes, interfaces and enums that assist with everyday Java development tasks.
The newest version!
/*
* Copyright 2010-2012 Craig S. Dickson (http://craigsdickson.com)
*
* 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.squeakysand.commons.lang;
import com.squeakysand.commons.beans.BeanHelper;
import com.squeakysand.commons.util.DictionaryMap;
import com.squeakysand.commons.util.IterableArray;
import com.squeakysand.commons.util.IterableEnumeration;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides utility methods to help override the {@link Object#toString()} method and/or create more meaningful logging
* output.
*/
public final class ToStringHelper {
private static class ArrayStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return klass.isArray();
}
@Override
public String render(Object target) {
@SuppressWarnings("rawtypes")
IterableArray> array = new IterableArray(target);
return ToStringHelper.toString(array);
}
}
private static class ClassStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Class.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return ((Class>) target).getName();
}
}
private static class DictionaryStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Dictionary.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
Dictionary,?> dictionary = (Dictionary,?>) target;
Map,?> map = new DictionaryMap(dictionary);
return ToStringHelper.toString(map);
}
}
private static class EnumerationStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Enumeration.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
@SuppressWarnings({"rawtypes", "unchecked"})
IterableEnumeration> iterableEnumeration = new IterableEnumeration((Enumeration>) target);
return ToStringHelper.toString(iterableEnumeration);
}
}
private static class FileStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return File.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return ((File) target).getPath();
}
}
private static class FloatStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Float.class.isAssignableFrom(klass) || float.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return Float.toString((Float) target) + 'F';
}
}
private static class IterableStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Iterable.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
StringBuilder sb = new StringBuilder("[");
Iterable> iterable = (Iterable>) target;
for (Object o : iterable) {
String tmp = ToStringHelper.toString(o);
sb.append(tmp);
sb.append(", ");
}
// chop off the last comma separator
if (sb.length() > 1) {
sb.setLength(sb.length() - 2);
}
sb.append("]");
return sb.toString();
}
}
private static class JavaBeanStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return true;
}
@Override
public String render(Object target) {
StringBuilder sb = new StringBuilder();
String name = target.getClass().getName();
int dotIndex = name.lastIndexOf('.');
if (dotIndex != -1) {
name = name.substring(dotIndex + 1);
}
sb.append(name);
String tmp = BeanHelper.getPropertiesString(target, true);
sb.append(tmp);
return sb.toString();
}
}
private static class LongStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Long.class.isAssignableFrom(klass) || long.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return Long.toString((Long) target) + 'L';
}
}
private static class MapStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return Map.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
Map,?> map = (Map,?>) target;
StringBuilder sb = new StringBuilder("{");
for (Entry,?> entry : map.entrySet()) {
Object key = entry.getKey();
String keyString = ToStringHelper.toString(key);
Object value = entry.getValue();
String valueString = ToStringHelper.toString(value);
sb.append(keyString);
sb.append(" : ");
sb.append(valueString);
sb.append(", ");
}
// chop off the last comma separator
if (sb.length() > 1) {
sb.setLength(sb.length() - 2);
}
sb.append("}");
return sb.toString();
}
}
private static interface StringRenderer {
boolean isAbleToRender(Class> klass);
String render(Object target);
}
private static class StringStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return String.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return '\"' + (String) target + '\"';
}
}
private static class UrlStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return URL.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return ((URL) target).toExternalForm();
}
}
private static class ValueOfStringRenderer implements StringRenderer {
@Override
public boolean isAbleToRender(Class> klass) {
return klass.isPrimitive() || klass.isEnum() || klass.isAnnotation() || Boolean.class.isAssignableFrom(klass)
|| Character.class.isAssignableFrom(klass) || Date.class.isAssignableFrom(klass) || Number.class.isAssignableFrom(klass);
}
@Override
public String render(Object target) {
return String.valueOf(target);
}
}
private static final Logger LOG = LoggerFactory.getLogger(ToStringHelper.class);
private static final String NULL = "null";
private static final List RENDERERS = new ArrayList();
static {
RENDERERS.add(new StringStringRenderer());
RENDERERS.add(new FloatStringRenderer());
RENDERERS.add(new LongStringRenderer());
RENDERERS.add(new MapStringRenderer());
RENDERERS.add(new DictionaryStringRenderer());
RENDERERS.add(new IterableStringRenderer());
RENDERERS.add(new EnumerationStringRenderer());
RENDERERS.add(new ArrayStringRenderer());
RENDERERS.add(new UrlStringRenderer());
RENDERERS.add(new FileStringRenderer());
RENDERERS.add(new ClassStringRenderer());
RENDERERS.add(new ValueOfStringRenderer());
RENDERERS.add(new JavaBeanStringRenderer());
}
private static final ThreadLocal DEPTH_CONTROLLER = new ThreadLocal() {
/**
* @see java.lang.ThreadLocal#get()
*/
@Override
public Integer get() {
Integer currentValue = super.get();
// LOG.debug("getting current depth value of {} for Thread: {}", currentValue,
// Thread.currentThread().getId());
return currentValue;
}
/**
* @see java.lang.ThreadLocal#remove()
*/
@Override
public void remove() {
// LOG.debug("removing depth value for Thread: {}", Thread.currentThread().getId());
super.remove();
}
/**
* @see java.lang.ThreadLocal#set(java.lang.Object)
*/
@Override
public void set(Integer value) {
// LOG.debug("setting depth value of {} for Thread: {}", value, Thread.currentThread().getId());
super.set(value);
}
/**
* @see java.lang.ThreadLocal#initialValue()
*/
@Override
protected Integer initialValue() {
// LOG.debug("getting new depth value for Thread: {}", Thread.currentThread().getId());
return 0;
}
};
private static final Integer MAX_DEPTH = 5;
private static final String DEFAULT_STRING = "...";
/**
* Generates a String representation of the passed in Object. This method will handle null values,
* objects, arrays of primitives and arrays of objects.
*
* @param o
* the target object, can be null or an array.
* @return a String representation of the target object.
*/
public static String toString(Object o) {
// LOG.debug("entering: {}", Thread.currentThread().getId());
String result = null;
if (o == null) {
result = NULL;
} else {
int currentDepth = DEPTH_CONTROLLER.get();
try {
if (currentDepth < MAX_DEPTH) {
Class> klass = o.getClass();
// LOG.debug("called for object of class {}", klass);
StringRenderer renderer = findRenderer(klass);
DEPTH_CONTROLLER.set(currentDepth + 1);
try {
result = renderer.render(o);
} catch (RuntimeException e) {
LOG.error(e.getLocalizedMessage(), e);
result = DEFAULT_STRING;
} finally {
DEPTH_CONTROLLER.set(currentDepth);
}
} else {
result = DEFAULT_STRING;
}
} finally {
if (DEPTH_CONTROLLER.get() == 0) {
DEPTH_CONTROLLER.remove();
}
}
}
// LOG.debug("returning result '{}' for Thread {}", result, Thread.currentThread().getId());
return result;
}
private static StringRenderer findRenderer(Class> klass) {
// LOG.debug("entering: {}", Thread.currentThread().getId());
// check to see if there is a converter directly registered for this class
StringRenderer result = null;
for (StringRenderer renderer : RENDERERS) {
if (renderer.isAbleToRender(klass)) {
result = renderer;
break;
}
}
// LOG.debug("for {}, renderer is {} in Thread {}", new Object[] {klass.getName(), result.getClass().getName(),
// Thread.currentThread().getId()});
return result;
}
private ToStringHelper() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy