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