Package net.aufdemrand.denizen.utilities.debugging

Source Code of net.aufdemrand.denizen.utilities.debugging.dB

package net.aufdemrand.denizen.utilities.debugging;

import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;

import net.aufdemrand.denizen.BukkitScriptEntryData;
import net.aufdemrand.denizen.Settings;

import net.aufdemrand.denizen.events.EventManager;
import net.aufdemrand.denizen.flags.FlagManager;
import net.aufdemrand.denizen.objects.Element;
import net.aufdemrand.denizen.objects.dObject;
import net.aufdemrand.denizen.objects.dScript;
import net.aufdemrand.denizen.scripts.ScriptEntry;
import net.aufdemrand.denizen.scripts.queues.ScriptQueue;
import net.aufdemrand.denizen.tags.TagManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import net.aufdemrand.denizencore.utilities.debugging.Debuggable;

/**
* Preferred method of outputting debugger information with Denizen and
* denizen-related plugins.
*
* Attempts to unify the style of reporting information to the Console and
* player with the use of color, headers, footers, and formatting.
*
*
* Example, this code:
*
* dB.echoDebug(DebugElement.Header, "Sample debug information");
* dB.echoDebug("This is an example of a piece of debug information. Parts and pieces " +
*         "of an entire debug sequence may be in completely different classes, so making " +
*         "a unified way to output to the console can make a world of difference with " +
*         "debugging and usability.");
* dB.echoDebug(DebugElement.Spacer);
* dB.echoDebug("Here are some examples of a few different ways to log with the logger.");
* dB.echoApproval("Notable events can nicely show success or approval.");
* dB.echoError("Your users will be able to easily distinguish problems.");
* dB.info("...and important pieces of information can be easily spotted.");
* dB.echoDebug(DebugElement.Footer);
*
*
* will produce this output (with color):
*
* 16:05:05 [INFO] +- Sample debug information ------+
* 16:05:05 [INFO] This is an example of a piece of debug information. Parts
*                   and pieces of an entire debug sequence may be in completely
*                   different classes, so making a unified way to output to the
*                   console can make a world of difference with debugging and
*                   usability.
* 16:05:05 [INFO]
* 16:05:05 [INFO] Here are some examples of a few different ways to log with the
*                      logger.
* 16:05:05 [INFO]  OKAY! Notable events can nicely show success or approval.
* 16:05:05 [INFO]  ERROR! Your users will be able to easily distinguish problems.
* 16:05:05 [INFO] +> ...and important pieces of information can easily be spotted.
* 16:05:05 [INFO] +---------------------+
*
*
* @author Jeremy Schroeder
*
*/
public class dB {

    public static boolean showDebug = Settings.showDebug();
    public static boolean showStackTraces = true;
    public static boolean showScriptBuilder = false;
    public static boolean showColor = true;
    public static boolean showEventsTrimming = false;
    public static boolean debugOverride = false;

    public static List<String> filter = new ArrayList<String>();

    public static boolean shouldTrim = true;
    public static boolean record = false;
    public static StringBuilder Recording = new StringBuilder();
    public static void toggle() { showDebug = !showDebug; }


    /**
     * Can be used with echoDebug(...) to output a header, footer,
     * or a spacer.
     *
     * DebugElement.Header = +- string description ------+
     * DebugElement.Spacer =
     * DebugElement.Footer = +--------------+
     *
     * Also includes color.
     */
    public static enum DebugElement {
        Header, Footer, Spacer
    }

    ////////////
    //  Public debugging methods, toggleable by checking extra criteria as implemented
    //  by the Debuggable interface, which usually checks a ScriptContainer's 'debug' node
    //////


    // <--[language]
    // @Name 'show_command_reports' player flag
    // @Group Useful flags

    // @Description
    // Giving a player the flag 'show_command_reports' will tell the Denizen dBugger to output
    // command reports to the player involved with the ScriptEntry. This can be useful for
    // script debugging, though it not an all-inclusive view of debugging information.
    //
    // To turn on and turn off the flag, just use:
    // <code>
    // /ex flag <player> show_command_reports
    // /ex flag <player> show_command_reports:!
    // </code>
    //
    // -->

    /**
     * Used by Commands to report how the supplied arguments were parsed.
     * Should be supplied a concatenated String with aH.debugObject() or dObject.debug() of all
     * applicable objects used by the Command.
     *
     * @param caller the object calling this debug
     * @param name the name of the command
     * @param report all the debug information related to the command
     */
    public static void report(Debuggable caller, String name, String report) {
        if (!showDebug) return;
        echo("<Y>+> <G>Executing '<Y>" + name + "<G>': "
                + trimMessage(report), caller);

        if (caller instanceof ScriptEntry) {
            if (((ScriptEntry) caller).hasPlayer()) {
                if (FlagManager.playerHasFlag(((ScriptEntry) caller).getPlayer(), "show_command_reports")) {
                    String message = "<Y>+> <G>Executing '<Y>" + name + "<G>': "
                            + trimMessage(report);

                    ((ScriptEntry) caller).getPlayer().getPlayerEntity()
                            .sendRawMessage(message.replace("<Y>", ChatColor.YELLOW.toString())
                                    .replace("<G>", ChatColor.DARK_GRAY.toString())
                                    .replace("<A>", ChatColor.AQUA.toString()));
                }
            }
        }
    }

    public static void echoDebug(Debuggable caller, DebugElement element) {
        if (!showDebug) return;
        echoDebug(caller, element, null);
    }


    // Used by the various parts of Denizen that output debuggable information
    // to help scripters see what is going on. Debugging an element is usually
    // for formatting debug information.
    public static void echoDebug(Debuggable caller, DebugElement element, String string) {
        if (!showDebug) return;
        StringBuilder sb = new StringBuilder(24);

        switch(element) {
            case Footer:
                sb.append(ChatColor.LIGHT_PURPLE).append("+---------------------+");
                break;

            case Header:
                sb.append(ChatColor.LIGHT_PURPLE).append("+- ").append(string).append(" ---------+");
                break;
        }

        echo(sb.toString(), caller);
    }


    // Used by the various parts of Denizen that output debuggable information
    // to help scripters see what is going on.
    public static void echoDebug(Debuggable caller, String message) {
        if (!showDebug) return;
        echo(ChatColor.LIGHT_PURPLE + " " + ChatColor.WHITE + trimMessage(message), caller);
    }


    // These methods are deprecated. Please instead supply a valid Debuggable reference,
    // which at this time is either a ScriptQueue, a ScriptEntry, or ScriptContainer.
    // If none of these are available, using dB.log(...)
    @Deprecated
    public static void echoDebug(String message) {
        echo(message, null);
    }

    @Deprecated
    public static void echoDebug(DebugElement de, String message) {
        echoDebug(null, de, message);
    }

    /////////////
    // Other public debugging methods (Always show when debugger is enabled)
    ///////


    /**
     * Shows an approval message (always shows, regardless of script debug mode, excluding debug fully off - use sparingly)
     * Prefixed with "OKAY! "
     * @param message the message to debug
     */
    public static void echoApproval(String message) {
        if (!showDebug) return;
        ConsoleSender.sendMessage(ChatColor.LIGHT_PURPLE + " " + ChatColor.GREEN + "OKAY! "
                + ChatColor.WHITE + message);
    }


    // <--[event]
    // @Events
    // script generates error
    //
    // @Triggers when a script generates an error.
    // @Context
    // <context.message> returns the error message.
    // <context.queue> returns the queue that caused the error, if any.
    // <context.script> returns the script that caused the error, if any.
    //
    // @Determine
    // "CANCELLED" to stop the error from showing in the console.
    // -->
    public static void echoError(String message) {
        echoError(null, message);
    }

    public static void echoError(ScriptQueue source, String message) {
        dScript script = null;
        if (source != null && source.getEntries().size() > 0 && source.getEntries().get(0).getScript() != null) {
            script = source.getEntries().get(0).getScript();
        }
        else if (source != null && source.getLastEntryExecuted() != null && source.getLastEntryExecuted().getScript() != null) {
            script = source.getLastEntryExecuted().getScript();
        }
        if (ThrowErrorEvent) {
            ThrowErrorEvent = false;
            Map<String, dObject> context = new HashMap<String, dObject>();
            context.put("message", new Element(message));
            if (source != null)
                context.put("queue", source);
            if (script != null)
                context.put("script", script);
            List<String> events = new ArrayList<String>();
            events.add("script generates error");
            if (script != null)
                events.add(script.identifySimple() + " generates error");
            ScriptEntry entry = (source != null ? source.getLastEntryExecuted(): null);
            String Determination = EventManager.doEvents(events, (entry != null ? ((BukkitScriptEntryData)entry.entryData).getNPC(): null),
                    (entry != null ? ((BukkitScriptEntryData)entry.entryData).getPlayer(): null), context, true);
            ThrowErrorEvent = true;
            if (Determination.equalsIgnoreCase("CANCELLED"))
                return;
        }
        if (!showDebug) return;
        ConsoleSender.sendMessage(ChatColor.LIGHT_PURPLE + " " + ChatColor.RED + "ERROR" +
                (script != null ? " in script '" + script.getName() + "'": "") + "! "
                + ChatColor.WHITE + trimMessage(message));
    }

    private static boolean ThrowErrorEvent = true;

    // <--[event]
    // @Events
    // server generates exception
    //
    // @Triggers when an exception occurs on the server.
    // @Context
    // <context.message> returns the Exception message.
    // <context.type> returns the type of the error. (EG, NullPointerException).
    // <context.queue> returns the queue that caused the exception, if any.
    //
    // @Determine
    // "CANCELLED" to stop the exception from showing in the console.
    // -->
    public static void echoError(Throwable ex) {
        echoError(null, ex);
    }

    public static void echoError(ScriptQueue source, Throwable ex) {
        if (ThrowErrorEvent) {
            ThrowErrorEvent = false;
            Map<String, dObject> context = new HashMap<String, dObject>();
            Throwable thrown = ex;
            if (ex.getCause() != null) {
                thrown = ex.getCause();
            }
            context.put("message", new Element(thrown.getMessage()));
            context.put("type", new Element(thrown.getClass().getSimpleName()));
            context.put("queue", source);
            ScriptEntry entry = (source != null ? source.getLastEntryExecuted(): null);
            String Determination = EventManager.doEvents(Arrays.asList("server generates exception"),
                    (entry != null ? ((BukkitScriptEntryData)entry.entryData).getNPC(): null), (entry != null ? ((BukkitScriptEntryData)entry.entryData).getPlayer(): null), context);
            ThrowErrorEvent = true;
            if (Determination.equalsIgnoreCase("CANCELLED"))
                return;
        }
        if (!showDebug) return;
        if (!showStackTraces) {
            dB.echoError(source, "Exception! Enable '/denizen debug -s' for the nitty-gritty.");
        }
        else {
            dB.echoError(source, "Internal exception was thrown!");
            ex.printStackTrace();
            if (dB.record) {
                String prefix = ConsoleSender.dateFormat.format(new Date()) + " [SEVERE] ";
                boolean first = true;
                while (ex != null) {
                    dB.Recording.append(URLEncoder.encode(prefix + (first ? "": "Caused by: ") + ex.toString() + "\n"));
                    for (StackTraceElement ste: ex.getStackTrace()) {
                        dB.Recording.append(URLEncoder.encode(prefix + ste.toString() + "\n"));
                    }
                    if (ex.getCause() == ex) {
                        return;
                    }
                    ex = ex.getCause();
                    first = false;
                }
            }
        }
    }

    private static final Map<Class<?>, String> classNameCache = new WeakHashMap<Class<?>, String>();

    public static void log(String message) {
        if (!showDebug) return;
        Class<?> caller = sun.reflect.Reflection.getCallerClass(2);
        String callerName = classNameCache.get(caller);
        if (callerName == null)
            classNameCache.put(caller, callerName = caller.getSimpleName());
        ConsoleSender.sendMessage(ChatColor.YELLOW + "+> ["
                + (callerName.length() > 16 ? callerName.substring(0, 12) + "..." : callerName) + "] "
                + ChatColor.WHITE + trimMessage(message));
    }


    public static void log(DebugElement element, String string) {
        if (!showDebug) return;
        StringBuilder sb = new StringBuilder(24);

        switch(element) {
            case Footer:
                sb.append(ChatColor.LIGHT_PURPLE).append("+---------------------+");
                break;

            case Header:
                sb.append(ChatColor.LIGHT_PURPLE).append("+- ").append(string).append(" ---------+");
                break;

            default:
                break;
        }

        ConsoleSender.sendMessage(sb.toString());
    }

    ///////////////
    // Private Helper Methods
    /////////


    // Some debug methods trim to keep super-long messages from hitting the console.
    private static String trimMessage(String message) {
        if (!shouldTrim) return message;
        int trimSize = Settings.trimLength();
        if (message.length() > trimSize)
            message = message.substring(0, trimSize - 1) + "... * snip! *";
        return message;
    }


    public static boolean shouldDebug(Debuggable caller) {
        if (debugOverride) {
            return true;
        }
        boolean should_send = true;

        // Attempt to see if the debug should even be sent by checking the
        // script container's 'debug' node.
        if (caller != null)
            try {

                if (filter.isEmpty())
                    should_send = caller.shouldDebug();

                else {
                    should_send = false;
                    for (String criteria : filter)
                        if (caller.shouldFilter(criteria)) {
                            should_send = true;
                            break;
                        }
                }

            } catch (Exception e) {
                // Had a problem determining whether it should debug, assume true.
                should_send = true;
            }
        return should_send;
    }

    // Handles checking whether the provided debuggable should submit to the debugger
    private static void echo(String string, Debuggable caller) {
        if (shouldDebug(caller)) ConsoleSender.sendMessage(string);
    }


    /**
     * ConsoleSender sends dScript debugging information to the logger
     * will attempt to intelligently wrap any debug information that is more
     * than one line. This is used by the dB static methods which do some
     * additional formatting.
     */
    private static class ConsoleSender {

        // Bukkit CommandSender sends color nicely to the logger, so we'll use that.
        static CommandSender commandSender = null;
        public static SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        static boolean skipFooter = false;

        // Use this method for sending a message
        public static void sendMessage(String string) {
            if (commandSender == null) commandSender = Bukkit.getServer().getConsoleSender();

            // These colors are used a lot in the debugging of commands/etc, so having a few shortcuts is nicer
            // than having a bunch of ChatColor.XXXX
            string = TagManager.cleanOutputFully(string
                    .replace("<Y>", ChatColor.YELLOW.toString())
                    .replace("<G>", ChatColor.DARK_GRAY.toString())
                    .replace("<A>", ChatColor.AQUA.toString()));

            // 'Hack-fix' for disallowing multiple 'footers' to print in a row
            if (string.equals(ChatColor.LIGHT_PURPLE + "+---------------------+")) {
                if (!skipFooter) skipFooter = true;
                else { return; }
            } else skipFooter = false;

            // Create buffer for wrapping debug text nicely. This is mostly needed for Windows logging.
            String[] words = string.split(" ");
            StringBuilder buffer = new StringBuilder();
            int length = 0;
            int width = Settings.consoleWidth();
            for (String word : words) { // # of total chars * # of lines - timestamp
                int strippedLength = ChatColor.stripColor(word).length() + 1;
                if (length + strippedLength  < width) {
                    buffer.append(word).append(" ");
                    length = length + strippedLength;
                } else {
                    // Increase # of lines to account for
                    length = strippedLength;
                    // Leave spaces to account for timestamp and indent
                    buffer.append("\n                   ").append(word).append(" ");
                }                 // [01:02:03 INFO]:
            }

            String result = buffer.toString();
            // Record current buffer to the to-be-submitted buffer
            if (dB.record) dB.Recording.append(URLEncoder.encode(dateFormat.format(new Date())
                    + " [INFO] " + result.replace(ChatColor.COLOR_CHAR, (char)0x01) + "\n"));

            // Send buffer to the player
            commandSender.sendMessage(showColor ? result : ChatColor.stripColor(result));
        }
    }
}
TOP

Related Classes of net.aufdemrand.denizen.utilities.debugging.dB

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.