    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.imaging.internal;
    018
    019import java.awt.color.ICC_Profile;
    020import java.io.File;
    021import java.text.DateFormat;
    022import java.text.SimpleDateFormat;
    023import java.util.ArrayList;
    024import java.util.Calendar;
    025import java.util.Date;
    026import java.util.List;
    027import java.util.Locale;
    028import java.util.Map;
    029import java.util.logging.Level;
    030import java.util.logging.Logger;
    031
    032/**
    033 * Internal-only debug class. Used for collecting extra information when parsing or
    034 * modifying images or metadata. These methods are useful for troubleshooting and
    035 * issue analysis, but this should not be used directly by end-users, nor extended
    036 * in any way. This may change or be removed at any time.
    037 */
    038public final class Debug {
    039
    040    private static final Logger LOGGER = Logger.getLogger(Debug.class.getName());
    041
    042    // public static String newline = System.getProperty("line.separator");
    043    private static final String NEWLINE = "\r\n";
    044    private static long counter;
    045
    046    public static void debug(final String message) {
    047        if (LOGGER.isLoggable(Level.FINEST)) {
    048            LOGGER.finest(message);
    049        }
    050    }
    051
    052    public static void debug() {
    053        if (LOGGER.isLoggable(Level.FINEST)) {
    054            LOGGER.finest(NEWLINE);
    055        }
    056    }
    057
    058    private static String getDebug(final String message, final int[] v) {
    059        final StringBuilder result = new StringBuilder();
    060
    061        if (v == null) {
    062            result.append(message + " (" + null + ")" + NEWLINE);
    063        } else {
    064            result.append(message + " (" + v.length + ")" + NEWLINE);
    065            for (final int element : v) {
    066                result.append("\t" + element + NEWLINE);
    067            }
    068            result.append(NEWLINE);
    069        }
    070        return result.toString();
    071    }
    072
    073    private static String getDebug(final String message, final byte[] v) {
    074        final int max = 250;
    075        return getDebug(message, v, max);
    076    }
    077
    078    private static String getDebug(final String message, final byte[] v, final int max) {
    079
    080        final StringBuilder result = new StringBuilder();
    081
    082        if (v == null) {
    083            result.append(message + " (" + null + ")" + NEWLINE);
    084        } else {
    085            result.append(message + " (" + v.length + ")" + NEWLINE);
    086            for (int i = 0; i < max && i < v.length; i++) {
    087                final int b = 0xff & v[i];
    088
    089                char c;
    090                if (b == 0 || b == 10 || b == 11 || b == 13) {
    091                    c = ' ';
    092                } else {
    093                    c = (char) b;
    094                }
    095
    096                result.append("\t" + i + ": " + b + " (" + c + ", 0x"
    097                        + Integer.toHexString(b) + ")" + NEWLINE);
    098            }
    099            if (v.length > max) {
    100                result.append("\t..." + NEWLINE);
    101            }
    102
    103            result.append(NEWLINE);
    104        }
    105        return result.toString();
    106    }
    107
    108    private static String getDebug(final String message, final char[] v) {
    109        final StringBuilder result = new StringBuilder();
    110
    111        if (v == null) {
    112            result.append(message + " (" + null + ")" + NEWLINE);
    113        } else {
    114            result.append(message + " (" + v.length + ")" + NEWLINE);
    115            for (final char element : v) {
    116                result.append("\t" + element + " (" + (0xff & element) + ")" + NEWLINE);
    117            }
    118            result.append(NEWLINE);
    119        }
    120        return result.toString();
    121    }
    122
    123    private static void debug(final String message, final Map<?, ?> map) {
    124        debug(getDebug(message, map));
    125    }
    126
    127    private static String getDebug(final String message, final Map<?, ?> map) {
    128        final StringBuilder result = new StringBuilder();
    129
    130        if (map == null) {
    131            return message + " map: " + null;
    132        }
    133
    134        final List<Object> keys = new ArrayList<>(map.keySet());
    135        result.append(message + " map: " + keys.size() + NEWLINE);
    136        for (int i = 0; i < keys.size(); i++) {
    137            final Object key = keys.get(i);
    138            final Object value = map.get(key);
    139            result.append("\t" + i + ": '" + key + "' -> '" + value + "'" + NEWLINE);
    140        }
    141
    142        result.append(NEWLINE);
    143
    144        return result.toString();
    145    }
    146
    147    private static String byteQuadToString(final int bytequad) {
    148        final byte b1 = (byte) ((bytequad >> 24) & 0xff);
    149        final byte b2 = (byte) ((bytequad >> 16) & 0xff);
    150        final byte b3 = (byte) ((bytequad >> 8) & 0xff);
    151        final byte b4 = (byte) ((bytequad >> 0) & 0xff);
    152
    153        final char c1 = (char) b1;
    154        final char c2 = (char) b2;
    155        final char c3 = (char) b3;
    156        final char c4 = (char) b4;
    157        // return new String(new char[] { c1, c2, c3, c4 });
    158        final StringBuilder buffer = new StringBuilder(31);
    159        buffer.append(new String(new char[]{c1, c2, c3, c4}));
    160        buffer.append(" bytequad: ");
    161        buffer.append(bytequad);
    162        buffer.append(" b1: ");
    163        buffer.append(b1);
    164        buffer.append(" b2: ");
    165        buffer.append(b2);
    166        buffer.append(" b3: ");
    167        buffer.append(b3);
    168        buffer.append(" b4: ");
    169        buffer.append(b4);
    170
    171        return buffer.toString();
    172    }
    173
    174    public static void debug(final String message, final Object value) {
    175        if (value == null) {
    176            debug(message, "null");
    177        } else if (value instanceof char[]) {
    178            debug(message, (char[]) value);
    179        } else if (value instanceof byte[]) {
    180            debug(message, (byte[]) value);
    181        } else if (value instanceof int[]) {
    182            debug(message, (int[]) value);
    183        } else if (value instanceof String) {
    184            debug(message, (String) value);
    185        } else if (value instanceof List) {
    186            debug(message, (List<?>) value);
    187        } else if (value instanceof Map) {
    188            debug(message, (Map<?, ?>) value);
    189        } else if (value instanceof ICC_Profile) {
    190            debug(message, (ICC_Profile) value);
    191        } else if (value instanceof File) {
    192            debug(message, (File) value);
    193        } else if (value instanceof Date) {
    194            debug(message, (Date) value);
    195        } else if (value instanceof Calendar) {
    196            debug(message, (Calendar) value);
    197        } else {
    198            debug(message, value.toString());
    199        }
    200    }
    201
    202    private static void debug(final String message, final byte[] v) {
    203        debug(getDebug(message, v));
    204    }
    205
    206    private static void debug(final String message, final char[] v) {
    207        debug(getDebug(message, v));
    208    }
    209
    210    private static void debug(final String message, final Calendar value) {
    211        final DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss", Locale.ENGLISH);
    212        debug(message, (value == null) ? "null" : df.format(value.getTime()));
    213    }
    214
    215    private static void debug(final String message, final Date value) {
    216        final DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss", Locale.ENGLISH);
    217        debug(message, (value == null) ? "null" : df.format(value));
    218    }
    219
    220    private static void debug(final String message, final File file) {
    221        debug(message + ": " + ((file == null) ? "null" : file.getPath()));
    222    }
    223
    224    private static void debug(final String message, final ICC_Profile value) {
    225        debug("ICC_Profile " + message + ": " + ((value == null) ? "null" : value.toString()));
    226        if (value != null) {
    227            debug("\t getProfileClass: " + byteQuadToString(value.getProfileClass()));
    228            debug("\t getPCSType: " + byteQuadToString(value.getPCSType()));
    229            debug("\t getColorSpaceType() : " + byteQuadToString(value.getColorSpaceType()));
    230        }
    231    }
    232
    233    private static void debug(final String message, final int[] v) {
    234        debug(getDebug(message, v));
    235    }
    236
    237    private static void debug(final String message, final List<?> v) {
    238        final String suffix = " [" + counter++ + "]";
    239
    240        debug(message + " (" + v.size() + ")" + suffix);
    241        for (final Object aV : v) {
    242            debug("\t" + aV.toString() + suffix);
    243        }
    244        debug();
    245    }
    246
    247    private static void debug(final String message, final String value) {
    248        debug(message + " " + value);
    249    }
    250
    251    public static void debug(final Throwable e) {
    252        debug(getDebug(e));
    253    }
    254
    255    public static void debug(final Throwable e, final int value) {
    256        debug(getDebug(e, value));
    257    }
    258
    259    private static String getDebug(final Throwable e) {
    260        return getDebug(e, -1);
    261    }
    262
    263    private static String getDebug(final Throwable e, final int max) {
    264        final StringBuilder result = new StringBuilder(35);
    265
    266        final SimpleDateFormat timestamp = new SimpleDateFormat(
    267                "yyyy-MM-dd kk:mm:ss:SSS", Locale.ENGLISH);
    268        final String datetime = timestamp.format(new Date()).toLowerCase();
    269
    270        result.append(NEWLINE);
    271        result.append("Throwable: "
    272                + ((e == null) ? "" : ("(" + e.getClass().getName() + ")"))
    273                + ":" + datetime + NEWLINE);
    274        result.append("Throwable: " + ((e == null) ? "null" : e.getLocalizedMessage()) + NEWLINE);
    275        result.append(NEWLINE);
    276
    277        result.append(getStackTrace(e, max));
    278
    279        result.append("Caught here:" + NEWLINE);
    280        result.append(getStackTrace(new Exception(), max, 1));
    281        // Debug.dumpStack();
    282        result.append(NEWLINE);
    283        return result.toString();
    284    }
    285
    286    private static String getStackTrace(final Throwable e, final int limit) {
    287        return getStackTrace(e, limit, 0);
    288    }
    289
    290    private static String getStackTrace(final Throwable e, final int limit, final int skip) {
    291        final StringBuilder result = new StringBuilder();
    292
    293        if (e != null) {
    294            final StackTraceElement[] stes = e.getStackTrace();
    295            if (stes != null) {
    296                for (int i = skip; i < stes.length && (limit < 0 || i < limit); i++) {
    297                    final StackTraceElement ste = stes[i];
    298
    299                    result.append("\tat " + ste.getClassName() + "."
    300                            + ste.getMethodName() + "(" + ste.getFileName()
    301                            + ":" + ste.getLineNumber() + ")" + NEWLINE);
    302                }
    303                if (limit >= 0 && stes.length > limit) {
    304                    result.append("\t..." + NEWLINE);
    305                }
    306            }
    307
    308            // e.printStackTrace(System.out);
    309            result.append(NEWLINE);
    310        }
    311
    312        return result.toString();
    313    }
    314
    315    private Debug() {
    316    }
    317}