View Javadoc

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 String 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 String ELEMENT_SEPARATOR = ", ";
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 "
90  						+ objectClass.getName() + ", " + e.getMessage());
91  
92  				unrenderableClasses.put(objectClass, now);
93  			}
94  		}
95  		String name = o.getClass().getName();
96  		return name + "@" + Integer.toHexString(o.hashCode());
97  	}
98  
99  	/**
100 	 * renderArray returns an array similar to a List. If the array type is an
101 	 * object they are rendered with "render(object)" for each. If the array
102 	 * type is a primitive each element is added directly to the string buffer
103 	 * collecting the result.
104 	 * 
105 	 * @param o
106 	 * @param objectClass
107 	 * @return
108 	 */
109 	private static StringBuffer renderArray(Object o, Class<?> objectClass) {
110 		Class<?> componentType = objectClass.getComponentType();
111 		StringBuffer sb = new StringBuffer(ARRAY_PREFIX);
112 
113 		if (componentType.isPrimitive() == false) {
114 			Object[] oa = (Object[]) o;
115 			for (int i = 0; i < oa.length; i++) {
116 				if (i > 0) {
117 					sb.append(ELEMENT_SEPARATOR);
118 				}
119 				sb.append(render(oa[i]));
120 			}
121 		} else {
122 			if (Boolean.TYPE.equals(componentType)) {
123 				boolean[] ba = (boolean[]) o;
124 				for (int i = 0; i < ba.length; i++) {
125 					if (i > 0) {
126 						sb.append(ELEMENT_SEPARATOR);
127 					}
128 					sb.append(ba[i]);
129 				}
130 			} else if (Integer.TYPE.equals(componentType)) {
131 				int[] ia = (int[]) o;
132 				for (int i = 0; i < ia.length; i++) {
133 					if (i > 0) {
134 						sb.append(ELEMENT_SEPARATOR);
135 					}
136 					sb.append(ia[i]);
137 				}
138 
139 			} else if (Long.TYPE.equals(componentType)) {
140 				long[] ia = (long[]) 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 (Double.TYPE.equals(componentType)) {
148 				double[] ia = (double[]) 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 (Float.TYPE.equals(componentType)) {
156 				float[] ia = (float[]) 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 			} else if (Character.TYPE.equals(componentType)) {
164 				char[] ia = (char[]) o;
165 				for (int i = 0; i < ia.length; i++) {
166 					if (i > 0) {
167 						sb.append(ELEMENT_SEPARATOR);
168 					}
169 					sb.append(ia[i]);
170 				}
171 			} else if (Short.TYPE.equals(componentType)) {
172 				short[] ia = (short[]) o;
173 				for (int i = 0; i < ia.length; i++) {
174 					if (i > 0) {
175 						sb.append(ELEMENT_SEPARATOR);
176 					}
177 					sb.append(ia[i]);
178 				}
179 			} else if (Byte.TYPE.equals(componentType)) {
180 				byte[] ia = (byte[]) o;
181 				for (int i = 0; i < ia.length; i++) {
182 					if (i > 0) {
183 						sb.append(ELEMENT_SEPARATOR);
184 					}
185 					sb.append(ia[i]);
186 				}
187 			}
188 		}
189 		sb.append(ARRAY_SUFFIX);
190 		return sb;
191 	}
192 }