nodeFace and node selection problem
blabno May 30, 2008 5:51 AMHello ! I have strange problem. I have a tree and I want to have this behaviour :
-when I click first time on a node the processSelection method should be called and node should be selected.
-when I click again on this node then again processSelection should be fired and node should be deselected.
Depending on my getNodeFace method implementation this works or not.
@Stateful
@Name("categoryManager")
public class CategoryManagerBean implements CategoryManagerLocal {
private int childrenIdSequence = 1;
@In
private EntityManager em;
@Logger
private Log log;
@DataModel
private List<OfferCategory> rootCategories;
@DataModel
private List<Class> categoryClasses;
@DataModelSelection("categoryClasses")
private Class categoryClass;
@Out(required = false)
private UITree systemTree;
@Out(required = false)
private UITree externalTree;
private TreeNode systemRootNode;
private TreeNode externalRootNode;
private TreeNode selectedSystemNode;
private TreeNode selectedExternalNode;
private TreeNode selectedRootNode;
@In
private FacesMessages facesMessages;
@In(required = false)
@Out(required = false)
private String categoryName;
private UITree selectedTree;
@Out(required = false)
private OfferCategory selectedCategory;
private Map<OfferCategory, TreeNode> categoriesNodes = new HashMap<OfferCategory, TreeNode>();
@Factory(value = "rootCategories")
public void loadRootCategories() {
rootCategories = em.createNamedQuery(OfferCategory.GET_ROOT_CATEGORIES_QUERY).getResultList();
}
@Factory(value = "categoryClasses")
public void loadCategoryClasses() {
categoryClasses = new ArrayList<Class>();
categoryClasses.add(AllegroOfferCategory.class);
categoryClasses.add(SystemOfferCategory.class);
log.debug("categoryClasses.size() : #0", categoryClasses.size());
}
@Begin(pageflow = "addRootCategory", flushMode = FlushModeType.MANUAL)
public void addRootCategory() {
}
public void prepareTrees() {
if (categoryClass == null) {
return;
}
if (!categoryClass.equals(SystemOfferCategory.class)) {
externalTree = getTree(categoryClass);
}
systemTree = getTree(SystemOfferCategory.class);
}
@Remove
public void remove() {
}
private MyTreeNodeImpl asTreeNode(OfferCategory category) {
MyTreeNodeImpl node = new MyTreeNodeImpl(childrenIdSequence++);
node.setData(category);
categoriesNodes.put(category, node);
log.debug("added TreeNode with id #0 containing category #1, hashCode=#2", node.getId(), category, category.hashCode());
for (OfferCategory subCategory : category.getSubcategories()) {
node.addChild(asTreeNode(subCategory));
}
return node;
}
private TreeNode getSelectedNode() {
if (selectedRootNode == null) {
return null;
}
return systemRootNode.equals(selectedRootNode) ? selectedSystemNode : selectedExternalNode;
}
private UITree getTree(Class<? extends OfferCategory> aClass) {
for (OfferCategory category : rootCategories) {
if (category.getClass().equals(aClass)) {
UITree tree = new HtmlTree();
TreeNode node = asTreeNode(category);
if (category.getClass().equals(SystemOfferCategory.class)) {
systemRootNode = node;
} else {
externalRootNode = node;
}
tree.setValue(node);
return tree;
}
}
return null;
}
public void processSelection(NodeSelectedEvent e) {
log.debug("processSelection");
selectedTree = (UITree) e.getComponent();
selectedCategory = null;
try {
if (selectedTree.getRowKey() == null) {
selectedCategory = null;
categoryName = "";
} else {
selectedCategory = (OfferCategory) selectedTree.getRowData();
categoryName = selectedCategory.getName();
}
} catch (IllegalStateException ex) {
log.debug(ex, ex);
return;
}
// if (selectedCategory.getRoot().equals(systemRootNode.getData())) {
if (selectedTree.equals(systemTree)) {
selectedSystemNode = selectedCategory != null ? selectedTree.getTreeNode() : null;
selectedTree = systemTree;
selectedRootNode = systemRootNode;
} else {
selectedExternalNode = selectedCategory != null ? selectedTree.getTreeNode() : null;
selectedTree = externalTree;
selectedRootNode = externalRootNode;
}
log.debug("processSelection done");
}
public void addCategory() {
if (selectedRootNode == null) {
facesMessages.addFromResourceBundle("selectTreeFirst");
return;
}
TreeNode selectedNode = systemRootNode.equals(selectedRootNode) ? selectedSystemNode : selectedExternalNode;
if (selectedNode == null) {
selectedNode = selectedRootNode;
}
selectedCategory = (OfferCategory) selectedNode.getData();
OfferCategory newCategory = null;
try {
newCategory = selectedCategory.getClass().newInstance();
} catch (Exception e) {
log.error(e);
}
newCategory.setName(categoryName);
newCategory.setParent(selectedCategory);
MyTreeNodeImpl newNode = new MyTreeNodeImpl(childrenIdSequence++);
newNode.setData(newCategory);
selectedNode.addChild(newNode.getId(), newNode);
expandNode(selectedTree, selectedNode);
categoriesNodes.put(newCategory, newNode);
log.debug("added TreeNode with id #0 containing category #1, hashCode=#2", newNode.getId(), newCategory, newCategory.hashCode());
}
public void saveCategory() {
if (selectedCategory == null) {
facesMessages.addFromResourceBundle("selectCategoryFirst");
return;
}
if (categoryName == null || categoryName.trim().equals("")) {
facesMessages.addFromResourceBundle("enterCategoryNameFirst");
return;
}
TreeNode selectedNode = categoriesNodes.remove(selectedCategory);
selectedCategory.setName(categoryName);
categoriesNodes.put(selectedCategory, selectedNode);
}
public void removeCategory() {
if (selectedCategory == null) {
facesMessages.addFromResourceBundle("selectCategoryFirst");
return;
}
MyTreeNodeImpl selectedNode = (MyTreeNodeImpl) categoriesNodes.remove(selectedCategory);
if (selectedNode != null) {
selectedNode.getParent().removeChild(selectedNode.getId());
}
/**
* category cannot be modified before node is retrived from map, since modifications change hashCode
*/
selectedCategory.setParent(null);
selectedCategory = null;
if (selectedNode.equals(selectedSystemNode)) {
selectedSystemNode = null;
} else {
selectedExternalNode = null;
}
}
public void mapCategory() {
if (selectedSystemNode == null || selectedExternalNode == null) {
facesMessages.addFromResourceBundle("selectTowNodesFirst");
return;
}
SystemOfferCategory systemCategory = (SystemOfferCategory) selectedSystemNode.getData();
ExternalOfferCategory externalCategory = (ExternalOfferCategory) selectedExternalNode.getData();
externalCategory.getMapping().add(systemCategory);
}
public String getNodeFace(OfferCategory category) {
// if (category instanceof ExternalOfferCategory) {
// ExternalOfferCategory externalCategory = (ExternalOfferCategory) category;
// return externalCategory.getMapping().isEmpty() ? "notMapped" : "mapped";
// } else {
// return "";
// }
if (category instanceof SystemOfferCategory) {
if (selectedExternalNode == null) {
return "";
} else {
return ((ExternalOfferCategory) selectedExternalNode.getData()).getMapping().contains(category) ? "mapped" : "";
}
}
if (category == null || getSelectedNode() == null) {
return "normal";
}
ExternalOfferCategory externalCategory = (ExternalOfferCategory) category;
return externalCategory.getMapping().isEmpty() ? "notMapped" : "mapped";
}
private void expandNode(UITree tree, TreeNode node) {
List<Object> path = new ArrayList<Object>();
while (node != null) {
path.add(((MyTreeNodeImpl) node).getId());
node = node.getParent();
}
Collections.reverse(path);
path.remove(0); // dont expand rootnode, it is not displayed
TreeState state = (TreeState) tree.getComponentState();
for (int i = 0; i < path.size(); i++) {
List<Object> list = new ArrayList<Object>();
for (int n = 0; n <= i; n++) {
list.add(path.get(n));
}
ListRowKey key = new ListRowKey(list);
try {
state.expandNode(tree, key);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
ListRowKey key = new ListRowKey(path);
state.setSelected(key);
}
public Boolean adviseNodeSelected(UITree tree) {
return tree.getTreeNode().equals(getSelectedNode());
}
public static class MyTreeNodeImpl extends TreeNodeImpl {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
public MyTreeNodeImpl() {
}
public MyTreeNodeImpl(Object id) {
setId(id);
}
public void addChild(MyTreeNodeImpl node) {
addChild(node.getId(), node);
}
}
}
<h:form id="editOfferCategory">
<a4j:outputPanel id="trees">
<a4j:outputPanel ajaxRendered="true">
<h:messages/>
</a4j:outputPanel>
<a4j:status startText="Request in progress..." stopText="Request finished"/>
<table>
<tr>
<td>
#{systemTree.value.data.name}
<rich:tree id="systemTree"
nodeSelectListener="#{categoryManager.processSelection}"
ajaxSubmitSelection="true"
switchType="ajax"
binding="#{systemTree}"
var="item"
reRender="lalala"
adviseNodeSelected="#{categoryManager.adviseNodeSelected}"
>
<rich:treeNode>#{item.name}</rich:treeNode>
</rich:tree>
</td>
<td class="buttons">
<a4j:commandButton value="#{messages.map}" action="#{categoryManager.mapCategory}" reRender="trees"/>
<br />
<h:outputLink value="#" onmouseup="showButton('addButton')">
#{messages.add}
<rich:componentControl for="categoryProperties" operation="show" event="onclick"/>
</h:outputLink>
<br />
<h:commandLink action="#{categoryManager.removeCategory()}">#{messages.remove}</h:commandLink>
</td>
<td>
#{externalTree.value.data.name}
<rich:tree id="externalTree"
nodeSelectListener="#{categoryManager.processSelection}"
ajaxSubmitSelection="true"
switchType="ajax"
binding="#{externalTree}"
var="item"
nodeFace="#{categoryManager.getNodeFace(item)}"
reRender="lalala"
adviseNodeSelected="#{categoryManager.adviseNodeSelected}"
>
<rich:treeNode type="normal" onmouseup="showButton('saveButton')">
normal #{item.name}
<rich:componentControl for="categoryProperties" operation="show" event="ondblclick"/>
</rich:treeNode>
<rich:treeNode type="notMapped" nodeClass="notMapped" onmouseup="showButton('saveButton')">
notMapped #{item.name}
<rich:componentControl for="categoryProperties" operation="show" event="ondblclick"/>
</rich:treeNode>
<rich:treeNode type="mapped" nodeClass="mapped" onmouseup="showButton('saveButton')">
mapped #{item.name}
<rich:componentControl for="categoryProperties" operation="show" event="ondblclick"/>
</rich:treeNode>
</rich:tree>
</td>
</tr>
</table>
</a4j:outputPanel>
<a4j:outputPanel ajaxRendered="true">
<h:outputText value="Selected category : #{categoryName}" id="lalala"/>
</a4j:outputPanel>
</h:form>
Now depending on my getNodeFace second click fires processSelection or not.
Following works, but nodeFace does not get changed properly (it stays the same) :
public String getNodeFace(OfferCategory category) {
if (category == null || getSelectedNode() == null) {
return "normal";
}
if (category instanceof ExternalOfferCategory) {
ExternalOfferCategory externalCategory = (ExternalOfferCategory) category;
return externalCategory.getMapping().isEmpty() ? "notMapped" : "mapped";
}
}
This does not work since second click does not trigger processSelection.
public String getNodeFace(OfferCategory category) {
if (category instanceof ExternalOfferCategory) {
ExternalOfferCategory externalCategory = (ExternalOfferCategory) category;
return externalCategory.getMapping().isEmpty() ? "notMapped" : "mapped";
} else {
return "";
}
}
Which brings me to a conclusion that if the nodeFace is not changed then processSelection is not fired, is it right ?
The working implementation is wrong from my business point of view and I would like to use the second one. What shall I do or where is my mistake ?