Skip to main content

[FYI] Tree renderering: auto-unwrap of userObject

No replies
Anonymous

As you might have noticed, I'm currently in yet another go for enhancing
SwingX renderering support, triggered partly by the old, but newly
revived requirement for a grand-unified-string-representation across
components (tree, table, list ...) and functionality (searching,
highlighting, filtering, more-to-come ...), the recent thread is

http://forums.java.net/jive/thread.jspa?threadID=36546&tstart=0

an older one linked from the wiki (yeah, I want you to give the swingx
wiki pages an eye occasionally ;-)

The api related to the string-rep itself is covered in the thread above,
so here's just an executive summary.

- "string-rep" means the textual representation of the cell context as
it appears on screen, that is as the ComponentProvider would configure a
"text" (or similar) property of the rendering component

- ComponentAdapter has the full set of getString/At(...) paralleling its
getValue/At(...) methods. The collection-view implementations delegate
as much as possible to their respective component.

- JXTable, JXTree, JXList have methods to access the string-rep in view
coordinates, like getStringAt(...) Their implementations delegate to the
appropriate renderer _without_ preparing it.

- The renderer must implement StringValue to support the string-rep
access. SwingX default renderers (the glue to legacy api - and farther
behind the scenes ComponentProvider) do so, core renderers need to be
subclassed to (that's just one method, but up to the developer - don't
recommend to use them at all).

haha ... too long for a summary, would have lost any real-world manager ;-)

SwingX tree rendering is special in that it is a kind of compound (ohhh
... that might in fact be a better name: CompoundProvider instead of
WrappingProvider?), which does the config of the tree-icon (like leaf,
openFolder..) itself and delegates the config of the node-content to a
contained provider.

And now - at last - we are back to the subject: auto-unwrap. It means to
try and dig down to the userObject, if the given value is a xNode (like
f.i. TreeTableNode) which often is for keeping the structure only. In
these cases, the rendering isn't really interested in the structure
(handled by the tree itself). To allow client code to control the
behaviour, WrappingProvider has a property unwrapUserObject which is
true by default.

The advantage of the auto-unwrap is that client code need not care about
the exact building blocks of a tree when it comes to string-related
highlighting, searching. Some code snippets (using files just because
they are easy to use in working code - there is some in
RendererVisualCheck), instead of the patternHighlighter you could do a
search with the pattern to see the live effects:

<br />
// use the file api directly to build the model<br />
TreeModel implicit = new FileSystemModel()<br />
// build an external structure with files a<br />
DefaultMutableTreeNode node = createNodeWrappedFiles(implicit);<br />
TreeModel explicit = new DefaultTreeModel(node);<br />
// gimme a flattened model of the files, the elements are of type File<br />
ListModel flattened = createListModel(implicit);</p>
<p>// config string rep (okay, unworldly - what else ;-)<br />
StringValue sv = new StringValue() {</p>
<p>    public String getString(Object value) {<br />
        if (value instanceof File) {<br />
            return FileSystemView.getFileSystemView()<br />
                       .getSystemDisplayName((File) value)<br />
               + " Type: "<br />
               + FileSystemView.getFileSystemView()<br />
                       .getSystemTypeDescription((File) value);<br />
        }<br />
        return TO_STRING.getString(value);<br />
    }</p>
<p>};</p>
<p>// use the string rep for rendering<br />
implicitTree.setCellRenderer(new DefaultTreeRenderer(sv));<br />
explicitTree.setCellRenderer(new DefaultTreeRenderer(sv));<br />
flattened.setCellRenderer(new DefaultListRenderer(sv);</p>
<p>// highlight based on the string rep<br />
// Note: this particular example is locale dependent<br />
String folderDescription = ".*ordner.*";<br />
PatternPredicate predicate = new<br />
PatternPredicate(Pattern.compile(folderDescription, 0), 0, -1);<br />
Highlighter hl = new ColorHighlighter(predicate, null, Color.RED);<br />
// add the highlighter to all<br />
implicitTree.addHighlighter(hl);<br />
...</p>
<p>

This concept of unwrapping-a-userObject works quite nicely: last week I
applied it in an example impl of transparent (no longer need to subclass
the view, which was my major quirk with Rob's) checkList support and
that went quite smoothly (see my incubator, foreign/rasto).

There's a slight glitch, though: the auto-unwrap is supported on the
string rep of the value only, not on the value itself. In the above
example, we might want to highlight all files which have not been
modified during the last year, a predicate (first go):

<br />
HighlightPredicate untouched = new HighlightPredicate() {</p>
<p>    public boolean isHighlighted(Component renderer,<br />
            ComponentAdapter adapter) {<br />
        if (!(adapter.getValue() instanceof File)) return false;<br />
        File file = (File) adapter.getValue();<br />
        Date date = new Date(file.lastModified());<br />
        return date.before(lastYear);<br />
    }</p>
<p>};</p>
<p>

That's doing nothing for the explict model (constructed out of nodes).
In that case, the predicate needs to unwrap as well, like f.i.:

<br />
HighlightPredicate valueBasedUnwrap = new HighlightPredicate() {</p>
<p>    public boolean isHighlighted(Component renderer,<br />
            ComponentAdapter adapter) {<br />
        File file = getUserObject(adapter.getValue());<br />
        if (file == null) return false;<br />
        Date date = new Date(file.lastModified());<br />
        return date.before(lastYear);<br />
    }</p>
<p>    private File getUserObject(Object value) {<br />
        if (value instanceof File) return (File) value;<br />
        if (value instanceof DefaultMutableTreeNode) {<br />
            return getUserObject(((DefaultMutableTreeNode)<br />
value).getUserObject());<br />
        }<br />
        if (value instanceof TreeTableNode) {<br />
            return getUserObject(((TreeTableNode) value).getUserObject());<br />
        }<br />
        return null;<br />
    }</p>
<p>};</p>
<p>

This is not really nice: the goal to make client code unaware of the
exact building blocks is reached for the string rep only, not for the
values themselves. Might even be considered a bit nasty as it might be
confusing for clients - they needn't care in the one case and do have to
in the other, confusing because it's inconsistent. Hmmm ...

Thoughts?

CU
Jeanette

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net