/*
* � Copyright IBM Corp. 2010
*
* Licensed 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.
*/
package com.ibm.xsp.extlib.renderkit.html_extended.outline.tree;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.complex.ValueBindingObjectImpl;
import com.ibm.xsp.component.xp.XspEventHandler;
import com.ibm.xsp.extlib.component.util.EventHandlerUtil;
import com.ibm.xsp.extlib.renderkit.html_extended.FacesRendererEx;
import com.ibm.xsp.extlib.tree.ITree;
import com.ibm.xsp.extlib.tree.ITreeNode;
import com.ibm.xsp.extlib.tree.ITreeNodeDefaultValues;
import com.ibm.xsp.extlib.tree.ITreeRenderer;
import com.ibm.xsp.extlib.tree.impl.AbstractTreeContext;
import com.ibm.xsp.util.HtmlUtil;
import com.ibm.xsp.util.JSUtil;
/**
* Base class that renders a tree.
* <p>
* </p>
* @author priand
*/
public abstract class AbstractTreeRenderer extends ValueBindingObjectImpl implements ITreeRenderer, ITreeNodeDefaultValues {
private static final long serialVersionUID = 1L;
// ==========================================================================
// From FacesRendererEx
// ==========================================================================
// Internal debug flag
public static final boolean DEBUG = FacesRendererEx.DEBUG;
protected Object getProperty(int prop) {
return null;
}
protected void newLine(ResponseWriter w) throws IOException {
JSUtil.writeln(w);
}
protected void newLine(ResponseWriter w, String comment) throws IOException {
if(DEBUG && comment!=null) {
w.writeComment(comment);
}
JSUtil.writeln(w);
}
protected static class TreeContextImpl extends AbstractTreeContext {
UIComponent component;
XspEventHandler handler;
String clientIdSuffix;
private boolean outerTagEmitted;
public TreeContextImpl(UIComponent component, ITree tree, ITreeNodeDefaultValues treeNodeDefault) {
super(tree, treeNodeDefault);
this.component = component;
if(component!=null) {
this.handler = EventHandlerUtil.findHandler(component,"onItemClick"); // $NON-NLS-1$
}
}
public UIComponent getComponent() {
return component;
}
public String getClientIdSuffix() {
return clientIdSuffix;
}
public void setClientIdSuffix(String clientIdSuffix) {
this.clientIdSuffix = clientIdSuffix;
}
public String getClientId(FacesContext context, String prefix, int depth) {
UIComponent c = getComponent();
if(c!=null) {
String clientId = c.getClientId(context);
StringBuilder b = null;
if(StringUtil.isNotEmpty(clientIdSuffix)) {
if(b==null) {
b = new StringBuilder(32);
b.append(clientId);
}
b.append('_');
b.append(clientIdSuffix);
}
if(StringUtil.isNotEmpty(prefix)) {
if(b==null) {
b = new StringBuilder(32);
b.append(clientId);
}
b.append('_');
b.append(prefix);
// We ignore the first index as it is always '0' (the tree...)
// so we start the index then at 1
if(depth>0) {
int cc = Math.min(depth+1, count);
for(int i=1; i<cc; i++) {
b.append('_');
b.append(contexts[i].getIndexInParent());
}
}
}
return b!=null ? b.toString() : clientId;
}
return null;
}
public XspEventHandler getOnClickHandler() {
return handler;
}
public boolean isOuterTagEmitted() {
return outerTagEmitted;
}
public void setOuterTagEmitted(boolean outerTagEmitted) {
this.outerTagEmitted = outerTagEmitted;
}
}
private String clientIdSuffix;
public AbstractTreeRenderer() {
}
public String getClientIdSuffix() {
return clientIdSuffix;
}
public void setClientIdSuffix(String clientIdSuffix) {
this.clientIdSuffix = clientIdSuffix;
}
// ===================================================================================
// Client id utility
// ===================================================================================
public String getClientId(FacesContext context, TreeContextImpl treeContext) {
return treeContext.getClientId(context, getClientIdSuffix(), 0);
}
public void writeClientIdIfNecessary(FacesContext context, ResponseWriter w, TreeContextImpl tree) throws IOException {
UIComponent component = tree.getComponent();
if(component!=null) {
if(HtmlUtil.isUserId(component.getId())) {
String clientId = tree.getClientId(context, /*prefix*/null, /*depth=0, the tree*/0);
w.writeAttribute("id", clientId, null); // $NON-NLS-1$ $NON-NLS-2$
tree.setOuterTagEmitted(true);
}
}
}
// ===================================================================================
// Tree rendering.
// ===================================================================================
public void render(FacesContext context, UIComponent component, ITree tree, ResponseWriter writer) throws IOException {
TreeContextImpl tc = new TreeContextImpl(component,tree,getTreeNodeDefault());
render(context, writer, tc);
}
public void render(FacesContext context, UIComponent component, String clientIdSuffix, ITree tree, ResponseWriter writer) throws IOException {
TreeContextImpl tc = new TreeContextImpl(component,tree,getTreeNodeDefault());
tc.setClientIdSuffix(clientIdSuffix);
render(context, writer, tc);
}
//public void render(FacesContext context, ResponseWriter writer, ITree.TreeContext treeContext) throws IOException;
public void render(FacesContext context, ResponseWriter writer, ITreeNode.TreeContext treeContext) throws IOException {
render(context, writer, (TreeContextImpl)treeContext);
}
protected void render(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
preRenderTree(context, writer, tree);
renderChildren(context, writer, tree);
postRenderTree(context, writer, tree);
}
protected void preRenderTree(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
}
protected void postRenderTree(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
}
// ===================================================================================
// NodeList rendering.
// ===================================================================================
protected void renderChildren(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
// Use lazy <ul> emitting in case of some children are rendered
boolean needPostRender = false;
if(tree.getNodeContext().hasChildren()) {
boolean expanded = tree.getNode()==null || tree.getNode().isExpanded();
if(renderCollapsedChildren() || expanded) {
AbstractTreeRenderer childrenRenderer = getChildrenRenderer(tree);
if(childrenRenderer!=null) {
ITreeNode.NodeIterator it = tree.getNodeContext().iterateChildren(0,Integer.MAX_VALUE);
if(it!=null) {
for( int i=0; it.hasNext(); i++) {
ITreeNode node = it.next();
if(node.isRendered()) {
preRenderList(context, writer, tree);
needPostRender = true;
break;
}
}
}
}
renderList(context, writer, tree);
if (needPostRender) {
postRenderList(context, writer, tree);
}
}
}
}
protected boolean renderCollapsedChildren() throws IOException {
return false;
}
protected void preRenderList(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
}
protected void postRenderList(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
}
protected void renderList(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException {
AbstractTreeRenderer childrenRenderer = getChildrenRenderer(tree);
if(childrenRenderer!=null) {
ITreeNode.NodeIterator it = tree.getNodeContext().iterateChildren(0,Integer.MAX_VALUE);
if(it!=null) {
for( int i=0; it.hasNext(); i++) {
ITreeNode node = it.next();
if(node.isRendered()) {
tree.push(node, i, !it.hasNext());
childrenRenderer.renderNode(context, writer, tree);
tree.pop();
}
}
}
}
}
protected AbstractTreeRenderer getChildrenRenderer(TreeContextImpl tree) {
// Recursively use the same renderer
return this;
}
// ===================================================================================
// Node rendering.
// ===================================================================================
protected abstract void renderNode(FacesContext context, ResponseWriter writer, TreeContextImpl tree) throws IOException;
// ============================================================================
// ITreeNodeDefault implementation
// 'this' is an ITreeNodeDefault.
// ============================================================================
public ITreeNodeDefaultValues getTreeNodeDefault() {
return this;
}
public String getNodeHref(ITreeNode node) {
return null;
}
public String getNodeImage(ITreeNode node) {
return null;
}
public String getNodeImageAlt(ITreeNode node) {
return null;
}
public String getNodeImageHeight(ITreeNode node) {
return null;
}
public String getNodeImageWidth(ITreeNode node) {
return null;
}
public String getNodeLabel(ITreeNode node) {
return null;
}
public String getNodeOnClick(ITreeNode node) {
return null;
}
public String getNodeRole(ITreeNode node) {
return null;
}
public String getNodeTitle(ITreeNode node) {
return null;
}
public String getNodeStyle(ITreeNode node) {
return null;
}
public String getNodeStyleClass(ITreeNode node) {
return null;
}
public String getNodeSubmitValue(ITreeNode node) {
return null;
}
public boolean isNodeEnabled(ITreeNode node) {
return true;
}
public boolean isNodeExpanded(ITreeNode node) {
return true;
}
public boolean isNodeRendered(ITreeNode node) {
return true;
}
public boolean isNodeSelected(ITreeNode node) {
return false;
}
// ===================================================================
// Styles to be used
// These methods are called by the renderer and ask the current node
// ===================================================================
protected String getContainerStyle(TreeContextImpl tree) {
ITreeNode n = tree.getNode();
return n!=null ? n.getStyle() : null;
}
protected String getContainerStyleClass(TreeContextImpl tree) {
ITreeNode n = tree.getNode();
return n!=null ? n.getStyleClass() : null;
}
protected String getItemStyle(TreeContextImpl tree, boolean enabled, boolean selected) {
ITreeNode n = tree.getNode();
return n!=null ? n.getStyle() : null;
}
protected String getItemStyleClass(TreeContextImpl tree, boolean enabled, boolean selected) {
ITreeNode n = tree.getNode();
return n!=null ? n.getStyleClass() : null;
}
protected String getItemRole(TreeContextImpl tree, boolean enabled, boolean selected) {
ITreeNode n = tree.getNode();
return n!=null ? n.getRole() : null;
}
protected String getItemTitle(TreeContextImpl tree, boolean enabled, boolean selected) {
ITreeNode n = tree.getNode();
return n!=null ? n.getTitle() : null;
}
// ============================================================================
// Node utility
// ============================================================================
public String findNodeOnClick(TreeContextImpl tree) {
String onclick = tree.getNode().getOnClick();
if(StringUtil.isNotEmpty(onclick)) {
return onclick;
}
XspEventHandler handler = tree.getOnClickHandler();
if(handler!=null) {
// This should be changed to also support partial refresh, which does not work in 8.5.2
String submitValue = tree.getNode().getSubmitValue();
if(StringUtil.isNotEmpty(submitValue)) {
return EventHandlerUtil.getEventScript(FacesContext.getCurrentInstance(), handler, submitValue);
}
}
return null;
}
}