1 /**
2 * Copyright (c) 2004-2011 QOS.ch
3 * All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 */
25 package org.slf4j.instrumentation;
26
27 import java.util.Map;
28 import java.util.WeakHashMap;
29
30 public class ToStringHelper {
31
32 /**
33 * Prefix to use at the start of the representation. Always used.
34 */
35 private static final String ARRAY_PREFIX = "[";
36
37 /**
38 * Suffix to use at the end of the representation. Always used.
39 */
40 private static final char ARRAY_SUFFIX = ']';
41
42 /**
43 * String separating each element when rendering an array. To be compatible
44 * with lists comma-space is used.
45 */
46
47 private static final char[] ELEMENT_SEPARATOR = ", ".toCharArray();
48
49 /**
50 * unrenderableClasses is essentially a Set of Class objects which has for
51 * some reason failed to render properly when invoked through a toString
52 * method call. To avoid memory leaks a data structure using weak references
53 * is needed, but unfortunately the runtime library does not contain a
54 * WeakHashSet class, so the behavior is emulated with a WeakHashmap with
55 * the class as the key, and a Long containing the value of
56 * System.currentTimeMilis when an instance of the class failed to render.
57 */
58
59 final static Map<Class<?>, Object> unrenderableClasses = new WeakHashMap<Class<?>, Object>();
60
61 /**
62 * Returns o.toString() unless it throws an exception (which causes it to be
63 * stored in unrenderableClasses) or already was present in
64 * unrenderableClasses. If so, the same string is returned as would have
65 * been returned by Object.toString(). Arrays get special treatment as they
66 * don't have usable toString methods.
67 *
68 * @param o
69 * incoming object to render.
70 * @return
71 */
72
73 public static String render(Object o) {
74 if (o == null) {
75 return String.valueOf(o);
76 }
77 Class<?> objectClass = o.getClass();
78
79 if (unrenderableClasses.containsKey(objectClass) == false) {
80 try {
81 if (objectClass.isArray()) {
82 return renderArray(o, objectClass).toString();
83 } else {
84 return o.toString();
85 }
86 } catch (Exception e) {
87 Long now = new Long(System.currentTimeMillis());
88
89 System.err.println("Disabling exception throwing class " + objectClass.getName() + ", " + e.getMessage());
90
91 unrenderableClasses.put(objectClass, now);
92 }
93 }
94 String name = o.getClass().getName();
95 return name + "@" + Integer.toHexString(o.hashCode());
96 }
97
98 /**
99 * renderArray returns an array similar to a List. If the array type is an
100 * object they are rendered with "render(object)" for each. If the array
101 * type is a primitive each element is added directly to the string buffer
102 * collecting the result.
103 *
104 * @param o
105 * @param objectClass
106 * @return
107 */
108 private static StringBuilder renderArray(Object o, Class<?> objectClass) {
109 Class<?> componentType = objectClass.getComponentType();
110 StringBuilder sb = new StringBuilder(ARRAY_PREFIX);
111
112 if (componentType.isPrimitive() == false) {
113 Object[] oa = (Object[]) o;
114 for (int i = 0; i < oa.length; i++) {
115 if (i > 0) {
116 sb.append(ELEMENT_SEPARATOR);
117 }
118 sb.append(render(oa[i]));
119 }
120 } else {
121 if (Boolean.TYPE.equals(componentType)) {
122 boolean[] ba = (boolean[]) o;
123 for (int i = 0; i < ba.length; i++) {
124 if (i > 0) {
125 sb.append(ELEMENT_SEPARATOR);
126 }
127 sb.append(ba[i]);
128 }
129 } else if (Integer.TYPE.equals(componentType)) {
130 int[] ia = (int[]) o;
131 for (int i = 0; i < ia.length; i++) {
132 if (i > 0) {
133 sb.append(ELEMENT_SEPARATOR);
134 }
135 sb.append(ia[i]);
136 }
137
138 } else if (Long.TYPE.equals(componentType)) {
139 long[] ia = (long[]) o;
140 for (int i = 0; i < ia.length; i++) {
141 if (i > 0) {
142 sb.append(ELEMENT_SEPARATOR);
143 }
144 sb.append(ia[i]);
145 }
146 } else if (Double.TYPE.equals(componentType)) {
147 double[] ia = (double[]) o;
148 for (int i = 0; i < ia.length; i++) {
149 if (i > 0) {
150 sb.append(ELEMENT_SEPARATOR);
151 }
152 sb.append(ia[i]);
153 }
154 } else if (Float.TYPE.equals(componentType)) {
155 float[] ia = (float[]) o;
156 for (int i = 0; i < ia.length; i++) {
157 if (i > 0) {
158 sb.append(ELEMENT_SEPARATOR);
159 }
160 sb.append(ia[i]);
161 }
162 } else if (Character.TYPE.equals(componentType)) {
163 char[] ia = (char[]) o;
164 for (int i = 0; i < ia.length; i++) {
165 if (i > 0) {
166 sb.append(ELEMENT_SEPARATOR);
167 }
168 sb.append(ia[i]);
169 }
170 } else if (Short.TYPE.equals(componentType)) {
171 short[] ia = (short[]) o;
172 for (int i = 0; i < ia.length; i++) {
173 if (i > 0) {
174 sb.append(ELEMENT_SEPARATOR);
175 }
176 sb.append(ia[i]);
177 }
178 } else if (Byte.TYPE.equals(componentType)) {
179 byte[] ia = (byte[]) o;
180 for (int i = 0; i < ia.length; i++) {
181 if (i > 0) {
182 sb.append(ELEMENT_SEPARATOR);
183 }
184 sb.append(ia[i]);
185 }
186 }
187 }
188 sb.append(ARRAY_SUFFIX);
189 return sb;
190 }
191 }