blob: a893377a6b960f8f68202a8ddc26700f75489545 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= DevFaqNodesDecorating
:jbake-type: wiki
:jbake-tags: wiki, devfaq, needsreview
:jbake-status: published
:keywords: Apache NetBeans wiki DevFaqNodesDecorating
:description: Apache NetBeans wiki DevFaqNodesDecorating
:toc: left
:toc-title:
:syntax: true
=== Decorating an entire tree of Nodes
Say you have a reference to the root of a tree of `Node` instances, and you want to add icons or actions to those nodes. First, what you *do not* do is call `setDisplayName` or any other setter on that Node (unless you created it - the point here is that it is rude and can have bad side effects to call setters on random Nodes somebody else created - setters in APIs are bugs - the fact that Node has them is a historical artifact, not proper design).
If you own the component that will display the Nodes, this sort of thing is very easily done by subclassing `FilterNode` and overriding the appropriate methods (e.g. `getActions()`, `getIcon()`, etc.), wrapping the original node inside your `FilterNode`. Now let's say that the Node you want to decorate builds out its children in a lazy fashion, that is, only when the user expands the tree in some tree view. How would you decorate that node and all of its children, without traversing the entire tree and effectively undoing the benefits of the lazy population of the tree?
Fortunately, while this sounds rather challenging, it turns out to be surprisingly easy and simple to achieve. The trick is to subclass the `FilterNode.Children` class and override the `copyNode()` method. Below is a short example:
[source,java]
----
class NodeProxy extends FilterNode {
public NodeProxy(Node original) {
super(original, new ProxyChildren(original));
}
// add your specialized behavior here...
}
class ProxyChildren extends FilterNode.Children {
public ProxyChildren(Node owner) {
super(owner);
}
protected Node copyNode(Node original) {
return new NodeProxy(original);
}
}
----
As you can see, `NodeProxy` is intended to wrap around another `Node` and provide some additional appearance or behavioral changes (e.g. different icons or actions). The fun part is the `ProxyChildren` class. While very short and simple, it provides that critical ability for our `NodeProxy` to act as a decorator for not only the root node, but all of its children, and their children, and so on, without having to traverse the entire tree at once.
While `FilterNode` should NOT be used to insert additional nodes at the beginning or end of the list (see its JavaDoc), it can be easily used to filter out some of the children nodes. For instance, this refinement of `ProxyChildren` overrides the `createNodes()` method and conditionally selects the children nodes by submitting them to a custom `accept()` method:
[source,java]
----
class ProxyChildren extends FilterNode.Children {
public ProxyChildren (Node owner) {
super(owner);
}
@Override
protected Node copyNode (Node original){
return new NodeProxy(original);
}
@Override
protected Node[] createNodes (Object object) {
List<Node> result = new ArrayList<Node>();
for (Node node : super.createNodes(object)) {
if (accept(node)) {
result.add(node);
}
}
return result.toArray(new Node[0]);
}
private boolean accept (Node node) {
// ...
}
}
----
Below a complete example of a `FileFilteredNode` that can be used to show a file hierarchy where only a subset of files is shown, selected by means of the standard `java.io.FileFilter` class:
[source,java]
----
class FileFilteredNode extends FilterNode {
static class FileFilteredChildren extends FilterNode.Children {
private final FileFilter fileFilter;
public FileFilteredChildren (Node owner, FileFilter fileFilter) {
super(owner);
this.fileFilter = fileFilter;
}
@Override
protected Node copyNode (Node original) {
return new FileFilteredNode(original, fileFilter);
}
@Override
protected Node[] createNodes (Object object) {
List<Node> result = new ArrayList<Node>();
for (Node node : super.createNodes(object)) {
DataObject dataObject = (DataObject)node.getLookup().lookup(DataObject.class);
if (dataObject != null) {
FileObject fileObject = dataObject.getPrimaryFile();
File file = FileUtil.toFile(fileObject);
if (fileFilter.accept(file)) {
result.add(node);
}
}
}
return result.toArray(new Node[result.size()]);
}
}
public FileFilteredNode (Node original, FileFilter fileFilter) {
super(original, new FileFilteredChildren(original, fileFilter));
}
}
----
Note that if you're showing the filtered nodes in a tree view according to the code above, you might find expansion handles on leaf nodes. link:http://openide.netbeans.org/servlets/ReadMsg?listName=dev&msgNo=11595[This thread from the dev@openide list] discusses some solutions to this problem.
=== Apache Migration Information
The content in this page was kindly donated by Oracle Corp. to the
Apache Software Foundation.
This page was exported from link:http://wiki.netbeans.org/DevFaqNodesDecorating[http://wiki.netbeans.org/DevFaqNodesDecorating] ,
that was last modified by NetBeans user J.boesl
on 2010-08-19T14:20:51Z.
*NOTE:* This document was automatically converted to the AsciiDoc format on 2018-02-07, and needs to be reviewed.