1   package org.slf4j.instrumentation;
2   
3   import java.util.Map;
4   import java.util.WeakHashMap;
5   
6   public class ToStringHelper {
7   
8   	/**
9   	 * Prefix to use at the start of the representation. Always used.
10  	 */
11  	private static final String ARRAY_PREFIX = "[";
12  
13  	/**
14  	 * Suffix to use at the end of the representation. Always used.
15  	 */
16  	private static final String ARRAY_SUFFIX = "]";
17  
18  	/**
19  	 * String separating each element when rendering an array. To be compatible
20  	 * with lists comma-space is used.
21  	 */
22  
23  	private static final String ELEMENT_SEPARATOR = ", ";
24  
25  	/**
26  	 * unrenderableClasses is essentially a Set of Class objects which has for
27  	 * some reason failed to render properly when invoked through a toString
28  	 * method call. To avoid memory leaks a data structure using weak references
29  	 * is needed, but unfortunately the runtime library does not contain a
30  	 * WeakHashSet class, so the behavior is emulated with a WeakHashmap with
31  	 * the class as the key, and a Long containing the value of
32  	 * System.currentTimeMilis when an instance of the class failed to render.
33  	 */
34  
35  	final static Map<Class<?>, Object> unrenderableClasses = new WeakHashMap<Class<?>, Object>();
36  
37  	/**
38  	 * Returns o.toString() unless it throws an exception (which causes it to be
39  	 * stored in unrenderableClasses) or already was present in
40  	 * unrenderableClasses. If so, the same string is returned as would have
41  	 * been returned by Object.toString(). Arrays get special treatment as they
42  	 * don't have usable toString methods.
43  	 * 
44  	 * @param o
45  	 *            incoming object to render.
46  	 * @return
47  	 */
48  
49  	public static String render(Object o) {
50  		if (o == null) {
51  			return String.valueOf(o);
52  		}
53  		Class<?> objectClass = o.getClass();
54  
55  		if (unrenderableClasses.containsKey(objectClass) == false) {
56  			try {
57  				if (objectClass.isArray()) {
58  					return renderArray(o, objectClass).toString();
59  				} else {
60  					return o.toString();
61  				}
62  			} catch (Exception e) {
63  				Long now = new Long(System.currentTimeMillis());
64  
65  				System.err.println("Disabling exception throwing class "
66  						+ objectClass.getName() + ", " + e.getMessage());
67  
68  				unrenderableClasses.put(objectClass, now);
69  			}
70  		}
71  		String name = o.getClass().getName();
72  		return name + "@" + Integer.toHexString(o.hashCode());
73  	}
74  
75  	/**
76  	 * renderArray returns an array similar to a List. If the array type is an
77  	 * object they are rendered with "render(object)" for each. If the array
78  	 * type is a primitive each element is added directly to the string buffer
79  	 * collecting the result.
80  	 * 
81  	 * @param o
82  	 * @param objectClass
83  	 * @return
84  	 */
85  	private static StringBuffer renderArray(Object o, Class<?> objectClass) {
86  		Class<?> componentType = objectClass.getComponentType();
87  		StringBuffer sb = new StringBuffer(ARRAY_PREFIX);
88  
89  		if (componentType.isPrimitive() == false) {
90  			Object[] oa = (Object[]) o;
91  			for (int i = 0; i < oa.length; i++) {
92  				if (i > 0) {
93  					sb.append(ELEMENT_SEPARATOR);
94  				}
95  				sb.append(render(oa[i]));
96  			}
97  		} else {
98  			if (Boolean.TYPE.equals(componentType)) {
99  				boolean[] ba = (boolean[]) o;
100 				for (int i = 0; i < ba.length; i++) {
101 					if (i > 0) {
102 						sb.append(ELEMENT_SEPARATOR);
103 					}
104 					sb.append(ba[i]);
105 				}
106 			} else if (Integer.TYPE.equals(componentType)) {
107 				int[] ia = (int[]) o;
108 				for (int i = 0; i < ia.length; i++) {
109 					if (i > 0) {
110 						sb.append(ELEMENT_SEPARATOR);
111 					}
112 					sb.append(ia[i]);
113 				}
114 
115 			} else if (Long.TYPE.equals(componentType)) {
116 				long[] ia = (long[]) o;
117 				for (int i = 0; i < ia.length; i++) {
118 					if (i > 0) {
119 						sb.append(ELEMENT_SEPARATOR);
120 					}
121 					sb.append(ia[i]);
122 				}
123 			} else if (Double.TYPE.equals(componentType)) {
124 				double[] ia = (double[]) o;
125 				for (int i = 0; i < ia.length; i++) {
126 					if (i > 0) {
127 						sb.append(ELEMENT_SEPARATOR);
128 					}
129 					sb.append(ia[i]);
130 				}
131 			} else if (Float.TYPE.equals(componentType)) {
132 				float[] ia = (float[]) o;
133 				for (int i = 0; i < ia.length; i++) {
134 					if (i > 0) {
135 						sb.append(ELEMENT_SEPARATOR);
136 					}
137 					sb.append(ia[i]);
138 				}
139 			} else if (Character.TYPE.equals(componentType)) {
140 				char[] ia = (char[]) o;
141 				for (int i = 0; i < ia.length; i++) {
142 					if (i > 0) {
143 						sb.append(ELEMENT_SEPARATOR);
144 					}
145 					sb.append(ia[i]);
146 				}
147 			} else if (Short.TYPE.equals(componentType)) {
148 				short[] ia = (short[]) o;
149 				for (int i = 0; i < ia.length; i++) {
150 					if (i > 0) {
151 						sb.append(ELEMENT_SEPARATOR);
152 					}
153 					sb.append(ia[i]);
154 				}
155 			} else if (Byte.TYPE.equals(componentType)) {
156 				byte[] ia = (byte[]) o;
157 				for (int i = 0; i < ia.length; i++) {
158 					if (i > 0) {
159 						sb.append(ELEMENT_SEPARATOR);
160 					}
161 					sb.append(ia[i]);
162 				}
163 			}
164 		}
165 		sb.append(ARRAY_SUFFIX);
166 		return sb;
167 	}
168 }