Mega Code Archive

 
Categories / Java / Development Class
 

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);         }     } }