blob: 8524fd0a2fb1eba7a823dc302743c5f939132ada [file] [log] [blame]
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
/*
* $Id: MenuBarRenderer.java,v 1.1 2005/11/03 02:59:58 SherryShen Exp $
*/
package components.renderkit;
import components.components.GraphComponent;
import components.model.Graph;
import components.model.Node;
import com.sun.org.apache.commons.logging.Log;
import com.sun.org.apache.commons.logging.LogFactory;
import jakarta.faces.FacesException;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIForm;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.event.ActionEvent;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
/**
* <p>Render our current value (which must be a <code>Graph</code>)
* as a menu bar, where the children of the root node are treated as individual
* menus, and grandchildren of the root node are the items on the main menus.
* A real application would display things as links to expand and contract
* items, including recursive submenus.</p>
*/
public class MenuBarRenderer extends BaseRenderer {
public static final String URL_PREFIX = "/faces";
public final static String FORM_NUMBER_ATTR =
"com.sun.faces.FormNumber";
private static Log log = LogFactory.getLog(MenuBarRenderer.class);
protected String treeClass = null;
protected String selectedClass = null;
protected String unselectedClass = null;
protected String clientId = null;
protected UIComponent component = null;
protected FacesContext context = null;
public void decode(FacesContext context, UIComponent component) {
Graph graph = null;
// if a node was clicked queue an ActionEvent.
Map requestParameterMap = (Map) context.getExternalContext().
getRequestParameterMap();
String path = (String) requestParameterMap.
get(component.getClientId(context));
if (path != null && path.length() != 0) {
component.getAttributes().put("path", path);
component.queueEvent(new ActionEvent(component));
if (log.isTraceEnabled()) {
log.trace("ActionEvent queued on Graph component for " + path);
}
}
}
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
Graph graph = null;
// Acquire the root node of the graph representing the menu
graph = (Graph) ((GraphComponent) component).getValue();
if (graph == null) {
throw new FacesException("Graph could not be located");
}
Node root = graph.getRoot();
if (root == null) {
throw new FacesException("Graph has no root node");
}
if (root.getChildCount() < 1) {
return; // Nothing to render
}
this.component = component;
this.context = context;
clientId = component.getClientId(context);
treeClass = (String) component.getAttributes().get("menuClass");
selectedClass = (String) component.getAttributes().get("selectedClass");
unselectedClass =
(String) component.getAttributes().get("unselectedClass");
// Render the menu bar for this graph
Iterator menus = null;
ResponseWriter writer = context.getResponseWriter();
writer.write("<table border=\"0\" cellspacing=\"3\" cellpadding=\"0\"");
if (treeClass != null) {
writer.write(" class=\"");
writer.write(treeClass);
writer.write("\"");
}
writer.write(">");
writer.write("\n");
writer.write("<tr>"); // For top level menu bar
menus = root.getChildren();
while (menus.hasNext()) {
Node menu = (Node) menus.next();
writer.write("<th bgcolor=\"silver\" align=\"left\">");
// The image links of the nodes that have children behave like
// command buttons causing the form to be submitted so the state of
// node can be toggled
if (menu.isEnabled()) {
writer.write("<a href=\"");
writer.write(getSubmitScript(menu.getPath(), context));
writer.write(" >");
writer.write(menu.getLabel());
writer.write("</a>");
} else {
writer.write(menu.getLabel());
}
writer.write("</th>");
}
writer.write("</tr>");
writer.write("<tr>"); // For any expanded menu(s)
menus = root.getChildren();
while (menus.hasNext()) {
Node menu = (Node) menus.next();
writer.write(
"<td bgcolor=\"silver\" align=\"left\" valign=\"top\">");
if (menu.isExpanded()) {
writer.write("<ul>");
Iterator items = menu.getChildren();
while (items.hasNext()) {
Node node = (Node) items.next();
writer.write("<li>");
// Render the label for this node (if any) as a
// link is the node is enabled.
if (node.getLabel() != null) {
writer.write(" ");
String labelStyle = null;
if (node.isSelected() && (selectedClass != null)) {
labelStyle = selectedClass;
} else if (!node.isSelected() &&
(unselectedClass != null)) {
labelStyle = unselectedClass;
}
if (node.isEnabled()) {
writer.write("<a href=\"");
// Note: we assume that the links do not act as
// command button, meaning they do not cause the
// form to be submitted.
writer.write(href(node.getAction()));
writer.write("\"");
if (labelStyle != null) {
writer.write(" class=\"");
writer.write(labelStyle);
writer.write("\"");
}
writer.write(">");
} else if (labelStyle != null) {
writer.write("<span class=\"");
writer.write(labelStyle);
writer.write("\">");
}
writer.write(node.getLabel());
if (node.getLabel() != null) {
writer.write("</a>");
} else if (labelStyle != null) {
writer.write("</span>");
}
}
writer.write("</li>");
// PENDING - marker for submenu
// PENDING - expanded submenu?
}
writer.write("</ul>");
} else {
writer.write("&nbsp;");
}
writer.write("</td>");
}
writer.write("<input type=\"hidden\" name=\"" + clientId + "\" />");
writer.write("</table>");
}
/**
* <p> Returns a string that is rendered as the value of
* <code>onmousedown</code> attribute. <code>onmousedown</code> event
* handler is used the track the node that was clicked using a hidden field,
* then submits the form so that we have the state information to
* reconstitute the tree.
*/
protected String getSubmitScript(String path, FacesContext context) {
UIForm uiform = getMyForm();
String formClientId = uiform.getClientId(context);
StringBuffer sb = new StringBuffer();
sb.append("#\" onclick=\"document.forms['" + formClientId + "']['" +
clientId + "'].value='" + path +
"';document.forms['" + formClientId + "'].submit()\"");
return sb.toString();
}
/**
* Returns the parent form of graph component.
*/
protected UIForm getMyForm() {
UIComponent parent = component.getParent();
while (parent != null) {
if (parent instanceof UIForm) {
break;
}
parent = parent.getParent();
}
return (UIForm) parent;
}
/**
* Returns a string that is rendered as the value of
* href attribute after prepending the contextPath if necessary.
*/
protected String href(String action) {
// if action does not start with a "/", it is considered an absolute
// URL and hence don't prepend contextPath.
if (action != null && !(action.startsWith("/"))) {
return action;
}
StringBuffer sb = new StringBuffer();
if (action.startsWith("/")) {
sb.append(context.getExternalContext().getRequestContextPath());
}
sb.append(action);
return (sb.toString());
}
}