001/**
002 * Copyright (c) 2004-2011 QOS.ch
003 * All rights reserved.
004 *
005 * Permission is hereby granted, free  of charge, to any person obtaining
006 * a  copy  of this  software  and  associated  documentation files  (the
007 * "Software"), to  deal in  the Software without  restriction, including
008 * without limitation  the rights to  use, copy, modify,  merge, publish,
009 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
010 * permit persons to whom the Software  is furnished to do so, subject to
011 * the following conditions:
012 *
013 * The  above  copyright  notice  and  this permission  notice  shall  be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
017 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
018 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
021 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023 *
024 */
025package org.slf4j.instrumentation;
026
027import java.util.Map;
028import java.util.WeakHashMap;
029
030public class ToStringHelper {
031
032    /**
033     * Prefix to use at the start of the representation. Always used.
034     */
035    private static final String ARRAY_PREFIX = "[";
036
037    /**
038     * Suffix to use at the end of the representation. Always used.
039     */
040    private static final char ARRAY_SUFFIX = ']';
041
042    /**
043     * String separating each element when rendering an array. To be compatible
044     * with lists comma-space is used.
045     */
046
047    private static final char[] ELEMENT_SEPARATOR = ", ".toCharArray();
048
049    /**
050     * unrenderableClasses is essentially a Set of Class objects which has for
051     * some reason failed to render properly when invoked through a toString
052     * method call. To avoid memory leaks a data structure using weak references
053     * is needed, but unfortunately the runtime library does not contain a
054     * WeakHashSet class, so the behavior is emulated with a WeakHashmap with
055     * the class as the key, and a Long containing the value of
056     * System.currentTimeMillis when an instance of the class failed to render.
057     */
058
059    final static Map<Class<?>, Object> unrenderableClasses = new WeakHashMap<>();
060
061    /**
062     * Returns o.toString() unless it throws an exception (which causes it to be
063     * stored in unrenderableClasses) or already was present in
064     * unrenderableClasses. If so, the same string is returned as would have
065     * been returned by Object.toString(). Arrays get special treatment as they
066     * don't have usable toString methods.
067     * 
068     * @param o
069     *            incoming object to render.
070     * @return
071     */
072
073    public static String render(Object o) {
074        if (o == null) {
075            return String.valueOf(o);
076        }
077        Class<?> objectClass = o.getClass();
078
079        if (unrenderableClasses.containsKey(objectClass) == false) {
080            try {
081                if (objectClass.isArray()) {
082                    return renderArray(o, objectClass).toString();
083                } else {
084                    return o.toString();
085                }
086            } catch (Exception e) {
087                Long now = Long.valueOf(System.currentTimeMillis());
088
089                System.err.println("Disabling exception throwing class " + objectClass.getName() + ", " + e.getMessage());
090
091                unrenderableClasses.put(objectClass, now);
092            }
093        }
094        String name = o.getClass().getName();
095        return name + "@" + Integer.toHexString(o.hashCode());
096    }
097
098    /**
099     * 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}