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 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.currentTimeMillis when an instance of the class failed to render.
57       */
58  
59      final static Map<Class<?>, Object> unrenderableClasses = new WeakHashMap<>();
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 = Long.valueOf(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 }