Basic Customization > User Interface Customization > Presenting Information in the UI > Windchill Client Architecture Tree > Solution > Solution Elements > Implementing Tree Component without using DataSources
  
Implementing Tree Component without using DataSources
To implement Tree component without DataSources, TreeHandler interface needs to be implemented. TreeHandlerAdapter provides default implementation. TreeHandler is a JAVA interface ,which is responsible for populating the tree content.Following methods are provided by TreeHandler :
ModelContext getModelContext() :- Get the handler's model context
void setModelContext(ModelContext mc) throws WTException :- Sets the contextual information about the tree that is being built, like the descriptor, command bean etc. This should be initialized before data is requested from the handler.
List getRootNodes() throws WTException :- Get the list of root nodes for the tree.
Map<Object,List> getNodes(List parents) throws WTException :- Get a mapping of the child nodes for each of the parent nodes in the given list This is the only method that will be called for the expand action, so this method must be able to initialize the handler properly so that it can answer the question of what are the children of the parent.
boolean isExpandNeeded(Object node, int level) throws WTException :- Determines whether or not the given node needs to be expanded. The default implementation looks at the session state of the list of nodes already expanded or configured by the tag.
boolean hasChildren(Object node) throws WTException :- Determines whether or not the given node should show the expand norgie if its collapsed. Override this method to improve performance or to get custom behavior. The default implementation calls the getNodes and sees if the size is larger than 0.
void addExpandedNode(Object node) throws WTException :- Add a node to the expanded node list. This list is returned and used in session state to remember what the tree expansion is.
Trees need to be carefully created because of their recursive nature that causes many queries to the database. The Treehandler class can save tree state information as private attributes on the class and reused for each getNodes call. In addition, you can take advantage of knowing which tree rows are expanded by the user with the isExpandNeeded method.
TreeHandlerAdapter is an abstract class which implements TreeHandler which you can extent. Following is an example
getRootNodes() determine what the root nodes should be. The TreeHandler has access to the ModelContext, which can be used to access required information.
public List getRootNodes() throws WTException {

NmCommandBean cb = getModelContext().getNmCommandBean();

WTPart part;
NmOid oid = cb.getPageOid();
if (oid == null) {
log.debug("No oid found in request, trying GOLF_CART");
part = getGolfCart();
}else {
if (!oid.isA(WTPart.class)) {
throw new ClassCastException("Expected part, but was: " + oid);
}
part = (WTPart)oid.getRef();
}

if (part == null) {
log.debug("Couldn't get part");
return null;
}

configSpec = ConfigHelper.service.getConfigSpecFor(part);
return Collections.singletonList(part);
}
The method first retrieves the NmCommandBean from the model context and extracts the primary oid from it. If no oid was requested from the user, then it uses the GOLF_CART part instead by calling the getGolfCart method. The getGolfCart method simply queries and returns the GOLF_CART. If the user did specify an oid in the request parameter, it checks to make sure the oid is a WTPart. Otherwise, it throws a ClassCastException. If it’s the expected oid, it continues by retrieving the part from the oid via the getRef method. This method inflates the referenced object, which then can be cast into a WTPart. The last thing it checks is if the part retrieved either from the oid or the GOLF_CART is null and if so return null. The configSpec variable is assigned the ConfigSpec of the part using the ConfigHelper class. This helper class contains a service that helps obtain Iterated objects from Mastered objects. Lastly, an immutable List containing the part is returned.
getNodes(List parents) Get a mapping of the child nodes for each of the parent nodes in the given list. It can be called directly without calling the getRootNodes() method first. E.g. (expand action). This means that this method must be able to initialize the handler properly so that it can answer the question of what are the children of the parent.
The given example, generates a hierarchy of WTPart based on the WTPartUsageLinks.
public Map<Object,List> getNodes(List parents) throws WTException {
if (configSpec == null) {
configSpec = getDefaultConfigSpec();
}
Map<Object,List> result = new HashMap<Object,List>();
//API returns a 3D array where the 1st dim is the parent parts,
//the 2nd dim is the list of children for a given parent,
//and the 3rd dim is 2 element array w/the link obj at 0 and the child part at 1
Persistable[][][] all_children = WTPartHelper.service.getUsesWTParts(
new WTArrayList(parents), configSpec);
for (ListIterator i = parents.listIterator(); i.hasNext();) {
WTPart parent = (WTPart)i.next();
Persistable[][] branch = all_children[i.previousIndex()];
if (branch == null) {
continue;
}
List children = new ArrayList(branch.length);
result.put(parent,children);
for (Persistable[] child : branch) {
children.add(child[1]);
}
}
log.debug("ParentsToChildren: " + result);
return result;
}
private ConfigSpec getDefaultConfigSpec() throws WTException {
return ConfigHelper.service.getDefaultConfigSpecFor(WTPart.class);
}
Custom tree handlers need to be registered. Please refer Refererence to <DataUtilities Document> for information on how to register services. Following is a sample tree handler entry.
<Service context="default" name="com.ptc.core.components.beans.TreeHandler">
<Option requestor="your_object" selector="your_treeHandler"
serviceClass="your_treeHandler_class"
cardinality="duplicate"/>
</Service>
If you want to show non-persistables in a Tree, you are recommended to return Element for getRootNodes & getNodes methods. If your element doesn’t have a valid “ufid” value available or “obid” attribute, you need to provide NmObject for each row object. This can be done in two ways.
1. Author a DataUtility which extends DefaultNmObjectUtility and override its getTargetObject() method which will return a NmObject for the row object. Please refer to NmObject Utilities for more information.
2. Your Element will have an attribute named "nmObject" with its value an instanceof NmObject
Otherwise, to show non-persistables, you can write a wrapper class extending from NmObject and override getOid() method to return proper NmOid.