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