Eclipse Tips & Tricks: Detail Formatter

1. Your Situation

You find yourself debugging a Java app with Eclipse and you’re stepping through the code in the Debug perspective. You have the Variables view open and for most of the selected types you can see a reasonable toString() output below the variables list.

However, many types don’t have a type specific implementation of toString(). E.g., consider an array of objects. There’s no toString() on arrays. That’s why the variables view prints you some meaningless default text with a hashcode for such variables.

But did you know that you can change that by defining a custom toString() implementation that’s used by the variables view at runtime? Let me introduce you to the concept of Detail Formatters and learn about the Display view along the way, too.

2. Preparing a custom type evaluation

Lets start at a breakpoint in some arbitrary code as shown in the following screenshot. The debugger stopped within a for-loop that iterates over a collection of CellBindDesc elements. In the Variables view the variable desc is selected to see what’s inside. Unfortunately, the class CellBindDesc didn’t override toString(). That’s why the default gibberish is printed below the variables list .

Prepare a code snippet to create a String representation for a type.

You do not need to know all the details about the CellBindDesc. It’s enough to know that it’s a description of mapping a model element’s field to an uiElement, e.g. a String property to a Text field.
To find a specific field of a model element the CellBindDesc holds an array of model element properties that’s similar to a tree path. The first element is a reference to a property of the model root element that’s stored in the model field of CellBindDesc. The other elements are references to properties of the model tree.

So, what I actually want to see below the variables list is not the gibberish, but at least the path to the model property and a hint to which uiElement it is bound. Sounds like some StringBuilder code, right? To create and test this code on the fly you should open the Display view . Imagine this view as being a container of code statements that are inlined at the position of the current debug step. In case of the screenshot above that would be line 170.
But the code is not really inlined. Instead you can use the Inspect command to execute the selected statements at that debug step. You can run that command with [Ctrl]+[Shift]+[I].

As you can see in the screenshot all lines are selected and the last statement even is a return statement that concatenates stuff to a String. Running [Ctrl]+[Shift]+[I] on that selection will show the familiar Inspect popup containing a String node with the return value if you did it right.

As you can see in this example, the Display view is very powerful. It’s so important to me, that I always open it when debugging. By the way, code completion does work there, too.

3. Edit the Detail Formatter

Now, with the snippet being tested in the Display view and the result being what one would actually like to see at it’s time to configure the Detail Formatter. Open the context menu on the variable for which you wish to configure a Detail Formatter and select the Edit Detail Formatter… entry .

Provide the body for a toString() method.

In the Edit Detail Formatter dialog paste the snippet from the Display view and make sure the Detail Formatter is enabled.
After closing the dialog you should see the detail formatted output of your selected variable. In case of my example I’m now able to see the type, the model path and the ui element of each CellBindDesc directly below the variables list.

As the Detail Formatter is used for every instance of the type on which it is defined I am now able to step through the list via [F8]. No more need to check each property of the variable desc until I know all the values I need to tell if something is wrong. Ain’t that a great time safer? I bet it is!

The snippet of the Detail Formatter is evaluated and the resulting String is shown below the list of variables in the Variables view.

4. Happy Debugging

Things you should have learned:

  1. Always open the Display view when you’re in the Debug perspective and make use of it. You can even collect several statements there and select only the one(s) you need for inspection depending on your debug context.
  2. Write Detail Formatters for arrays and complex types to show more details in the Variables view. Detail Formatters do even override toString() implementations if there should be one!

Happy debugging!

8 comments to Eclipse Tips & Tricks: Detail Formatter

  • Tom H

    maybe we should start a github repo to collect together these formatters, as I have a couple for EObject formatting, and would probably benefit from some of the other users experience on what is important to show.

  • Tom H

    Here is my example of a formatter for IResourceDelta, (which is a little awkward because what is actually doing is obscured by a bunch of int mask values)


    StringBuffer sb = new StringBuffer();
    org.eclipse.core.resources.IResource res = getResource();
    switch (getKind()) {
    case IResourceDelta.ADDED:
    sb.append("Resource "+res.getFullPath()+" was added.").append("\n");
    break;
    case IResourceDelta.REMOVED:
    sb.append(res.getClass()+" "+res.getFullPath()+" was removed.").append("\n");
    break;
    case IResourceDelta.CHANGED:
    sb.append(res.getClass()+" "+res.getFullPath()+" was changed.").append("\n");
    break;
    case IResourceDelta.CONTENT:
    sb.append(res.getClass()+" "+res.getFullPath()+" changed content.").append("\n");
    break;
    case IResourceDelta.OPEN:
    sb.append(res.getClass()+" "+res.getFullPath()+" was opened.").append("\n");
    break;
    }

    int flags = getFlags();
    if ((flags & IResourceDelta.CONTENT) != 0) {
    sb.append("--> Content Change").append("\n");
    }
    if ((flags & IResourceDelta.REPLACED) != 0) {
    sb.append("--> REPLACED Replaced").append("\n");
    }
    if ((flags & IResourceDelta.MARKERS) != 0) {
    sb.append("--> Marker Change").append("\n");
    }
    if ((flags & IResourceDelta.DERIVED_CHANGED) != 0) {
    sb.append("--> DERIVED_CHANGED Change").append("\n");
    }
    if ((flags & IResourceDelta.DESCRIPTION) != 0) {
    sb.append("--> DESCRIPTION Change").append("\n");
    }
    if ((flags & IResourceDelta.OPEN) != 0) {
    sb.append("--> OPEN Change").append("\n");
    }
    if ((flags & IResourceDelta.TYPE) != 0) {
    sb.append("--> TYPE Change").append("\n");
    }
    if ((flags & IResourceDelta.SYNC) != 0) {
    sb.append("--> SYNC Change").append("\n");
    }
    if ((flags & IResourceDelta.REPLACED) != 0) {
    sb.append("--> REPLACED Change").append("\n");
    }
    if ((flags & IResourceDelta.MOVED_TO) != 0) {
    sb.append("--> MOVED_TO Change").append("\n");
    }
    if ((flags & IResourceDelta.MOVED_FROM) != 0) {
    sb.append("--> MOVED_FROM Change").append("\n");
    }
    if ((flags & IResourceDelta.COPIED_FROM) != 0) {
    sb.append("--> COPIED_FROM Change").append("\n");
    }
    if (flags == 0) {
    sb.append("--> flags zero").append("\n");
    }
    sb.append("flags were +"+flags);

    return sb.toString();

  • Tom H

    eclipse supports exporting and importing the detail formatter preferences manually via the file->export->preferences to its epf format.

    Buts its not very readable…

    #Tue Dec 04 01:11:05 GMT 2012
    file_export_version=3.0
    /instance/org.eclipse.jdt.debug.ui/org.eclipse.jdt.debug.ui.detail_formatters=org.eclipse.core.resources.IResourceDelta,\nStringBuffer sb \= new StringBuffer();\norg.eclipse.core.resources.IResource res \= getResource();\nswitch (getKind()) {\n case IResourceDelta.ADDED\:\n sb.append("Resource "+res.getFullPath()+" was added.").append("\\n");\n break;\n case IResourceDelta.REMOVED\:\n sb.append(res.getClass()+" "+res.getFullPath()+" was removed.").append("\\n");\n break;\n case IResourceDelta.CHANGED\:\n\t\t\tsb.append(res.getClass()+" "+res.getFullPath()+" was changed.").append("\\n");\n\t\t\tbreak;\n case IResourceDelta.CONTENT\:\n\t\t\tsb.append(res.getClass()+" "+res.getFullPath()+" changed content.").append("\\n");\n break;\n case IResourceDelta.OPEN\:\n\t\t\tsb.append(res.getClass()+" "+res.getFullPath()+" was opened.").append("\\n");\n break;\n}\n\nint flags \= getFlags();\nif ((flags & IResourceDelta.CONTENT) \!\= 0) {\n sb.append("--> Content Change").append("\\n");\n}\nif ((flags & IResourceDelta.REPLACED) \!\= 0) {\n sb.append("--> REPLACED Replaced").append("\\n");\n}\nif ((flags & IResourceDelta.MARKERS) \!\= 0) {\n sb.append("--> Marker Change").append("\\n");\n}\nif ((flags & IResourceDelta.DERIVED_CHANGED) \!\= 0) {\n sb.append("--> DERIVED_CHANGED Change").append("\\n");\n}\nif ((flags & IResourceDelta.DESCRIPTION) \!\= 0) {\n sb.append("--> DESCRIPTION Change").append("\\n");\n}\nif ((flags & IResourceDelta.OPEN) \!\= 0) {\n sb.append("--> OPEN Change").append("\\n");\n}\nif ((flags & IResourceDelta.TYPE) \!\= 0) {\n sb.append("--> TYPE Change").append("\\n");\n}\nif ((flags & IResourceDelta.SYNC) \!\= 0) {\n sb.append("--> SYNC Change").append("\\n");\n}\nif ((flags & IResourceDelta.REPLACED) \!\= 0) {\n sb.append("--> REPLACED Change").append("\\n");\n}\nif ((flags & IResourceDelta.MOVED_TO) \!\= 0) {\n sb.append("--> MOVED_TO Change").append("\\n");\n}\nif ((flags & IResourceDelta.MOVED_FROM) \!\= 0) {\n sb.append("--> MOVED_FROM Change").append("\\n");\n}\nif ((flags & IResourceDelta.COPIED_FROM) \!\= 0) {\n sb.append("--> COPIED_FROM Change").append("\\n");\n}\nif (flags \=\= 0) {\n sb.append("--> flags zero").append("\\n");\n}\nsb.append("flags were +"+flags);\n\nreturn sb.toString();\n,1,org.eclipse.emf.ecore.EObject,if (this \=\= null) return null;\n\nStringBuilder sb \= new StringBuilder();\n\njava.util.List attrs \= eClass().getEAllAttributes();\n\nsb.append("getting EObject Attributes\:("+attrs.size()+")");\n\nfor(org.eclipse.emf.ecore.EAttribute a \: attrs) {\n sb.append(a.getName() + " \= " + eGet(a)).append("\\n");\n}\n\norg.eclipse.emf.common.util.EList eRefs \= eClass().getEReferences();\n\nsb.append("getting EObject References\:("+eRefs.size()+")");\n\nfor (org.eclipse.emf.ecore.EReference eRef \: eRefs) {\n if (eGet(eRef) \!\= null) {\n sb.append(eRef.getName() + "\=" + eGet(eRef).toString()).append("\\n");\n }else{\n sb.append(eRef.getName() + "\=null").append("\\n");\n }\n}\n\nSystem.out.println("end of formatter13");\n\nreturn sb.toString();,1,org.w3c.dom.Node,if (this \=\= null) return null;\n\njavax.xml.transform.TransformerFactory tf \= javax.xml.transform.TransformerFactory.newInstance(); \njavax.xml.transform.Transformer transformer \= tf.newTransformer();\ntransformer.setOutputProperty( javax.xml.transform.OutputKeys.METHOD\u0000 "xml");\ntransformer.setOutputProperty("encoding"\u0000 encoding);\ntransformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT\u0000"yes");\ntransformer.setOutputProperty("{http\://xml.apache.org/xslt}indent-amount"\u0000 "3" );\n\njavax.xml.transform.dom.DOMSource source \= new javax.xml.transform.dom.DOMSource(this);\nif (source \=\= null) return "Corrupted XML document\: " + this.toString();\n\njava.io.StringWriter os \= new java.io.StringWriter();\njavax.xml.transform.stream.StreamResult result \= new javax.xml.transform.stream.StreamResult(os);\ntransformer.transform(source\u0000result);\n\nreturn os.toString ();\n,1
    \!/=
    @org.eclipse.jdt.debug.ui=3.6.200.v20121017-180556

  • Tom H

    editing the formatters via the eclipse preferences makes for a torturous round trip… So there is lots of scope for improvement for this. So the other horrible object for the debugger is the emf EObject, because it usually has a lot of stuff in it, that is tucked away from the top inspection level and needs some teasing out the values for EAttributes and EReferences etc

    • robert

      I agree, however, working with Qt/C++ at the moment there’s no such thing as a Detail Formatter in QtCreator, and I really value the tools we have at hand with Eclipse regardless there’s room for improvement or not. ;-) That you actually can do such things we do with the Detail Formatter is so amazing!

      • Tom H

        So I was inspired by looking at the eclim project (http://eclim.org/) and that now parts of the eclipse empire are breaking off like ice bergs (e.g. lots of people using tycho for building eclipse end-products, using maven for managing project dependencies etc) also github has now changed the workflow, so eclipse is just one of various tools that can be used to build projects hosted on github, as opposed to when eclipse was the big daddy, and team providers were just eclipse plugins.. ;-)

  • [...] JDT (for Java) has cool Detail Formatter where I can write my own special formatter for varibles (http://www.robertwloch.net/2012/01/eclipse-tips-tricks-detail-formatter/). Unfortunately this still does not exist for Eclipse CDT :-( Share [...]

Leave a Reply

  

  

  

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>