Debugging utility that reports, in a brute force manner, any internal data of a class instance : Debug « Development Class « Java






Debugging utility that reports, in a brute force manner, any internal data of a class instance

     
//----------------------------------------------------------------------------//
//                                                                            //
//                                D u m p e r                                 //
//                                                                            //
//  Copyright (C) Herve Bitteur 2000-2009. All rights reserved.               //
//  This software is released under the GNU General Public License.           //
//  Please contact users@audiveris.dev.java.net to report bugs & suggestions. //
//----------------------------------------------------------------------------//
//

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Map;

/**
 * Class <code>Dumper</code> is a debugging utility that reports, in a brute
 * force manner, any internal data of a class instance.
 *
 * <p> When used on a class instance, all class internal fields which are
 * considered as "relevant" are printed using their toString() method, then we
 * walk up the inheritance tree and repeat the same actions, until there is no
 * more superclass or until the superclass we have reached is considered as
 * non-relevant. </p>
 *
 * <p> A (super)class is considered "relevant" if the static method
 * <code>isClassRelevant(class)</code> returns true. This method can be
 * overridden in a subclass of Dumper to adapt to local needs. </p>
 *
 * <p> A field is considered "relevant" if the following condition if the method
 * <code>isFieldRelevant(field)</code> returns true. Similarly, the behavior of
 * this predicate can be customized by subclassing the Dumper class. </p>
 *
 * <p> There are several kinds of print outs available through subclassing. Each
 * of them export two public methods: <code>dump()</code> which prints the
 * result on default output stream, and <code>dumpOf()</code> which simply
 * returns the generated dump string.
 *
 * <ul> <li> <b>Column</b> a dump with one line per field </li>
 *
 * <li> <b>Row</b> a dump with all information on one row </li>
 *
 * <li> <b>Html</b> an Html stream with fields arranged in tables </li>
 *
 * </ul>
 *
 * Here are some examples of use:
 * <pre>
 * // Using the predefined static helper methods
 * Dumper.dump(myinstance);
 * Dumper.dump(myinstance, "My Title");
 * Dumper.dump(myinstance, "My Title", 2);
 * System.out.println(Dumper.dumpOf(myinstance));
 * System.out.println(Dumper.htmlDumpOf(myinstance));
 *
 *  // Using directly the Dumper subclasses
 * new Dumper.Column(myinstance).print();
 * System.out.println(new Dumper.Row(myinstance).toString());
 * display(new Dumper.Html(myinstance).toString());
 * </pre>
 *
 * @author Herv&eacute; Bitteur
 * @version $Id: Dumper.java,v 1.15 2009/03/03 19:45:51 hbitteur Exp $
 */
public abstract class Dumper
{
    //~ Instance fields --------------------------------------------------------

    /**
     * The object to be dumped
     */
    protected final Object obj;

    /**
     * The string buffer used as output
     */
    protected final StringBuffer sb;

    /**
     * Can we use HTML directives?
     */
    protected final boolean useHtml;

    /** Maximum number of collection items printed */
    private final int MAX_COLLECTION_INDEX = 9;

    /**
     * Class (beware, this variable is updated as we walk up the inheritance
     * tree)
     */
    protected Class cl;

    //~ Constructors -----------------------------------------------------------

    /**
     * Creates a new Dumper.
     *
     * @param obj the object instance to be dumped.
     */
    private Dumper (Object  obj,
                    boolean useHtml)
    {
        // (re)Allocate the string buffer
        sb = new StringBuffer(1024);

        // Cache the object & the related class
        this.obj = obj;
        this.useHtml = useHtml;
        cl = obj.getClass();
    }

    //~ Methods ----------------------------------------------------------------

    //-----------------//
    // isClassRelevant //
    //-----------------//
    /**
     * Predicate to determine if a given class is worth being printed. This
     * method could be overridden to reflect customized policy. Note that when
     * walking up the inheritance tree, the browsing is stopped as soon as a
     * non-relevant class is encountered.
     *
     * @param cl the class at stake
     *
     * @return true if found relevant
     */
    public static boolean isClassRelevant (Class cl)
    {
        //        return (cl != null) && !cl.getName()
        //                                  .startsWith("java.") &&
        //               !cl.getName()
        //                  .startsWith("javax.");
        return (cl != null) && cl.getName()
                                 .startsWith("omr.");
    }

    //-----------------//
    // isFieldRelevant //
    //-----------------//
    /**
     * Predicate to determine if a given field is worth being printed. This
     * method could be overridden to reflect customized policy.
     *
     * @param field the field at stake
     *
     * @return true if found relevant
     */
    public static boolean isFieldRelevant (Field field)
    {
        // We don't print static field since the Dumper is meant for instances
        if (Modifier.isStatic(field.getModifiers())) {
            return false;
        }

        // We don't print non-user visible entities
        if (field.getName()
                 .indexOf('$') != -1) {
            return false;
        }

        return true;
    }

    //------//
    // dump //
    //------//
    /**
     * Helper function that prints the internal data of an object onto the
     * standard output.
     *
     * @param obj the instance to dump
     */
    public static void dump (Object obj)
    {
        dump(obj, null, 0);
    }

    //------//
    // dump //
    //------//
    /**
     * Helper function that prints the internal data of an object onto the
     * standard output, with a specified left indentation level.
     *
     * @param obj   the instance to dump
     * @param level the indentation level (0 means no indentation)
     */
    public static void dump (Object obj,
                             int    level)
    {
        dump(obj, null, level);
    }

    //------//
    // dump //
    //------//
    /**
     * Helper function that prints the internal data of an object onto the
     * standard output, with the ability to print a related title
     *
     * @param obj   the object to dump
     * @param title the title to print beforehand
     */
    public static void dump (Object obj,
                             String title)
    {
        dump(obj, title, 0);
    }

    //------//
    // dump //
    //------//
    /**
     * Helper function that prints the internal data of an object onto the
     * standard output, with room for a title and left indentation.
     *
     * @param obj   the object to dump
     * @param title the title to print beforehand
     * @param level the indentation level (0 for no indent)
     */
    public static void dump (Object obj,
                             String title,
                             int    level)
    {
        new Column(obj, title, level).print();
    }

    //--------//
    // dumpOf //
    //--------//
    /**
     * Helper function that returns a line which contains the whole set of
     * internal data
     *
     * @param obj the object whose data is to be printed
     *
     * @return the string of data values
     */
    public static String dumpOf (Object obj)
    {
        return new Row(obj).toString();
    }

    //------------//
    // htmlDumpOf //
    //------------//
    /**
     * Helper function that prints a special kind of information string, using
     * HTML tags so that an html editor can easily render this.
     *
     * @param obj the object to dump
     *
     * @return the HTML string
     */
    public static String htmlDumpOf (Object obj)
    {
        return new Html(obj).toString();
    }

    //-------//
    // print //
    //-------//
    /**
     * Print the dump string onto the standard output
     */
    public void print ()
    {
        System.out.println(toString());
    }

    //----------//
    // toString //
    //----------//
    /**
     * Return the string buffer content
     *
     * @return the dump of the object as a string
     */
    @Override
    public String toString ()
    {
        // Do the processing
        processObject();

        // Return the final content of string buffer
        return sb.toString();
    }

    //------------------//
    // printClassEpilog //
    //------------------//
    /**
     * To be overridden so as to print the epilog of class data
     */
    protected void printClassEpilog ()
    {
    }

    //------------------//
    // printClassProlog //
    //------------------//
    /**
     * To be overridden so as to print the prolog of class data
     */
    protected void printClassProlog ()
    {
    }

    //----------------------//
    // printCollectionValue //
    //----------------------//
    protected void printCollectionValue (Collection col)
    {
        sb.append("[");

        int i = 0;

        for (Object obj : col) {
            if (i++ > 0) {
                sb.append(useHtml ? ",<br/>" : ",");
            }

            // Safeguard action when the object is a big collection
            if (i > MAX_COLLECTION_INDEX) {
                sb.append(" ... " + col.size() + " items");

                break;
            } else {
                sb.append(obj);
            }
        }

        sb.append("]");
    }

    //------------//
    // printField //
    //------------//
    /**
     * Basic printing of field name and value. The method can of course be
     * overridden.
     *
     * @param name  the field name
     * @param value the field value, which may be null
     */
    protected void printField (String name,
                               Object value)
    {
        if (value == null) {
            sb.append("null");
        } else {
            if (value instanceof Collection) {
                printCollectionValue((Collection) value);
            } else if (value instanceof Map) {
                printCollectionValue(((Map) value).entrySet());
            } else {
                sb.append(value.toString());
            }
        }
    }

    //--------------//
    // processClass //
    //--------------//
    private void processClass ()
    {
        // Class Prolog
        printClassProlog();

        // Process the class Fields
        for (Field field : cl.getDeclaredFields()) {
            processField(field);
        }

        // Class Epilog
        printClassEpilog();
    }

    //--------------//
    // processField //
    //--------------//
    private void processField (Field field)
    {
        // Check that we are really interested in printing this field out
        if (isFieldRelevant(field)) {
            // Override any access limitation
            field.setAccessible(true);

            try {
                // Retrieve field value in the object instance
                Object value = field.get(obj);

                // Print the field value as requested
                printField(field.getName(), value);
            } catch (IllegalAccessException ex) {
                // Cannot occur in fact, thanks to setAccessible
            }
        }
    }

    //---------------//
    // processObject //
    //---------------//
    private void processObject ()
    {
        do {
            // Process the class at hand
            processClass();

            // Walk up the inheritance tree
            cl = cl.getSuperclass();
        } while (isClassRelevant(cl));
    }

    //~ Inner Classes ----------------------------------------------------------

    //--------//
    // Column //
    //--------//
    /**
     * Class <code>Column</code> implements a Dumper where all fields are
     * presented in one column, each field on a separate line. The column can be
     * left indented, according to the specified indentation level.
     */
    public static class Column
        extends Dumper
    {
        //~ Static fields/initializers -----------------------------------------

        private static final String MEMBER_GAP = "   ";
        private static final String INDENT_GAP = ".  ";

        //~ Instance fields ----------------------------------------------------

        private final String       title;
        private final StringBuffer prefix;

        //~ Constructors -------------------------------------------------------

        public Column (Object obj,
                       String title,
                       int    level)
        {
            super(obj, false);

            // Cache the title
            if (title != null) {
                this.title = title;
            } else {
                this.title = "";
            }

            // Prepare indent prefix
            prefix = new StringBuffer(level * INDENT_GAP.length());

            for (int i = level; i > 0; i--) {
                prefix.append(INDENT_GAP);
            }
        }

        //~ Methods ------------------------------------------------------------

        @Override
        protected void printClassProlog ()
        {
            // We print the class name only for the lowest class in
            // heritance hierarchy
            if (obj.getClass() == cl) {
                sb.append("\n");
                sb.append(prefix)
                  .append(cl.getName());
                sb.append(" ")
                  .append(title)
                  .append(":");
            }
        }

        @Override
        protected void printField (String name,
                                   Object value)
        {
            sb.append("\n");
            sb.append(prefix)
              .append(MEMBER_GAP);
            sb.append(name)
              .append("=");
            super.printField(name, value);
        }
    }

    //------//
    // Html //
    //------//
    /**
     * Class <code>Html</code> implements a Dumper using HTML tags to present
     * fields in a table.
     */
    public static class Html
        extends Dumper
    {
        //~ Constructors -------------------------------------------------------

        protected Html (Object obj)
        {
            super(obj, true);
        }

        //~ Methods ------------------------------------------------------------

        @Override
        public String toString ()
        {
            // Style
            sb.append("<style> td {")
              .append(" font-family: Lucida Console, Verdana, sans-serif;")
              .append(" font-size: 9px;")
              .append(" font-style: normal;")
              .append("} </style>");

            // Table begin
            sb.append("<table border=0 cellpadding=3>");

            // The object
            super.processObject();

            // Table end
            sb.append("</table>");

            // Return the final content of string buffer
            return sb.toString();
        }

        @Override
        protected void printClassProlog ()
        {
            // Class name
            sb.append("<tr><td colspan=2><font color='BLUE'>")
              .append(cl.getName())
              .append("</font></td></tr>");
        }

        @Override
        protected void printField (String name,
                                   Object value)
        {
            // One table row per field
            sb.append("<tr>");

            // First the field name
            sb.append("<td align='right'><font color='RED'>")
              .append(name)
              .append("</font></td>");

            // Then the field value
            sb.append("<td>");
            super.printField(name, value);

            sb.append("</td>")
              .append("</tr>");
        }
    }

    //-----//
    // Row //
    //-----//
    /**
     * Class <code>Row</code> implements a Dumper where all fields are presented
     * on the same line.
     */
    public static class Row
        extends Dumper
    {
        //~ Constructors -------------------------------------------------------

        protected Row (Object obj)
        {
            super(obj, false);
        }

        //~ Methods ------------------------------------------------------------

        @Override
        protected void printClassEpilog ()
        {
            sb.append("}");
        }

        @Override
        protected void printClassProlog ()
        {
            // Class name
            sb.append("{");

            // Special annotation for superclass
            if (obj.getClass() != cl) {
                sb.append("from ");
            }

            sb.append(cl.getName())
              .append(":");
        }

        @Override
        protected void printField (String name,
                                   Object value)
        {
            sb.append(" ");
            sb.append(name)
              .append("=");
            super.printField(name, value);
        }
    }
}

   
    
    
    
    
  








Related examples in the same category

1.A simple logging facility.
2.Debug Utilities
3.Debug InputStream
4.Methods for printing Debug messages
5.Trace InputStream
6.Trace OutputStream
7.Debug Utility
8.Swing Console
9.How to do Benchmark
10.Methods for logging events
11.Printing indented text
12.Prints messages formatted for a specific line width.
13.Class providing static methods to log diagnostics
14.A bean that can be used to keep track of a counter
15.An integer synchronized counter class.
16.Counts down from a specified value the number of bytes actually read from the wrapped InputStream.
17.A long integer counter class
18.Logging class to record errors or unexpected behavior to a file
19.Handle obtaining string timestamps
20.Scans java source files in cvs tree and validates the license header
21.Debug Util
22.Array debug util
23.A simple frame that allows quick and easy visualisation of something