com.sun.faces.renderkit.html_basic.CommandLinkRenderer Maven / Gradle / Ivy
/*
* $Id: CommandLinkRenderer.java,v 1.46 2006/03/28 21:12:49 rlubke Exp $
*/
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* https://javaserverfaces.dev.java.net/CDDL.html or
* legal/CDDLv1.0.txt.
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* [Name of File] [ver.__] [Date]
*
* Copyright 2005 Sun Microsystems Inc. All Rights Reserved
*/
// CommandLinkRenderer.java
package com.sun.faces.renderkit.html_basic;
import java.io.IOException;
import java.util.Map;
import java.util.ArrayList;
import javax.faces.component.NamingContainer;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.component.UIForm;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import com.sun.faces.RIConstants;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.util.Util;
import com.sun.faces.util.MessageUtils;
import java.util.logging.Level;
/**
* CommandLinkRenderer is a class that renders the current value of
* UICommand as a HyperLink that acts like a Button.
*/
public class CommandLinkRenderer extends LinkRenderer {
//
// Protected Constants
//
// Separator character
//
// Class Variables
//
//
// Instance Variables
//
// Attribute Instance Variables
// Relationship Instance Variables
//
// Constructors and Initializers
//
//
// Class methods
//
//
// General Methods
//
//
// Methods From Renderer
//
public void decode(FacesContext context, UIComponent component) {
if (context == null || component == null) {
throw new NullPointerException(MessageUtils.getExceptionMessageString(
MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID));
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER,
"Begin decoding component " + component.getId());
}
UICommand command = (UICommand) component;
// If the component is disabled, do not change the value of the
// component, since its state cannot be changed.
if (Util.componentIsDisabledOrReadonly(component)) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("No decoding necessary since the component " +
component.getId() + " is disabled");
}
return;
}
String
clientId = command.getClientId(context),
paramName = getHiddenFieldName(context, command);
if (null == paramName) {
return;
}
Map requestParameterMap = context.getExternalContext()
.getRequestParameterMap();
String value = requestParameterMap.get(paramName);
if (value == null || value.equals("") || !clientId.equals(value)) {
return;
}
ActionEvent actionEvent = new ActionEvent(component);
component.queueEvent(actionEvent);
if (logger.isLoggable(Level.FINE)) {
logger.fine("This command resulted in form submission " +
" ActionEvent queued " + actionEvent);
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER,
"End decoding component " + component.getId());
}
}
public boolean getRendersChildren() {
return true;
}
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
if (context == null || component == null) {
throw new NullPointerException(
MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID));
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER,
"Begin encoding component " + component.getId());
}
UICommand command = (UICommand)component;
// suppress rendering if "rendered" property on the command is
// false.
if (!command.isRendered()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("End encoding component " + component.getId() +
" since " +
"rendered attribute is set to false ");
}
return;
}
boolean componentDisabled = false;
if (command.getAttributes().get("disabled") != null) {
if ((command.getAttributes().get("disabled")).equals(Boolean.TRUE)) {
componentDisabled = true;
}
}
Object form = getMyForm(component);
if (form == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("component '" + component.getId() +
"' must be enclosed inside a form ");
}
}
if (componentDisabled || getMyForm(component) == null) {
renderAsDisabled(context, command);
} else {
renderAsActive(context, command);
}
}
public void encodeChildren(FacesContext context, UIComponent component)
throws IOException {
if (context == null || component == null) {
throw new NullPointerException(
MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID));
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER,
"Begin encoding children " + component.getId());
}
// suppress rendering if "rendered" property on the component is
// false.
if (!component.isRendered()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("End encoding component " + component.getId() +
" since " +
"rendered attribute is set to false ");
}
return;
}
for (UIComponent kid : component.getChildren()) {
kid.encodeBegin(context);
if (kid.getRendersChildren()) {
kid.encodeChildren(context);
}
kid.encodeEnd(context);
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER,
"End encoding children " + component.getId());
}
}
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
if (context == null || component == null) {
throw new NullPointerException(
MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID));
}
UICommand command = (UICommand) component;
// suppress rendering if "rendered" property on the command is
// false.
if (!command.isRendered()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("End encoding component " + component.getId() +
" since " +
"rendered attribute is set to false ");
}
return;
}
ResponseWriter writer = context.getResponseWriter();
assert (writer != null);
String fieldName = getHiddenFieldName(context, component);
if (fieldName == null) {
writer.write(MessageUtils.getExceptionMessageString(
MessageUtils.COMMAND_LINK_NO_FORM_MESSAGE_ID));
writer.endElement("span");
return;
}
//Write Anchor inline elements
boolean componentDisabled = false;
if (component.getAttributes().get("disabled") != null) {
if ((component.getAttributes().get("disabled")).equals(Boolean.TRUE)) {
componentDisabled = true;
}
}
if (componentDisabled) {
writer.endElement("span");
return;
}
//Done writing Anchor element
writer.endElement("a");
renderHiddenFieldsAndScriptIfNecessary(context, writer, component, fieldName);
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER,
"End encoding component " + component.getId());
}
}
protected void renderAsActive(FacesContext context, UIComponent command)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
assert (writer != null);
String clientId = command.getClientId(context);
UIForm uiform = getMyForm(command);
if ( uiform == null ) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("component " + command.getId() +
" must be enclosed inside a form ");
}
return;
}
String formClientId = uiform.getClientId(context);
//Write Anchor attributes
//make link act as if it's a button using javascript
Param paramList[] = getParamList(context, command);
writer.startElement("a", command);
writeIdAttributeIfNecessary(context, writer, command);
writer.writeAttribute("href", "#", "href");
RenderKitUtils.renderPassThruAttributes(context,
writer,
command,
new String[] { "target" });
RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, command);
// render onclick
String userOnclick = (String)command.getAttributes().get("onclick");
StringBuffer sb = new StringBuffer(128);
boolean userSpecifiedOnclick = (userOnclick != null && !"".equals(userOnclick));
// if user specified their own onclick value, we are going to
// wrap their js and the injected js each in a function and
// execute them in a choose statement, if the user didn't specify
// an onclick, the original logic executes unaffected
if (userSpecifiedOnclick) {
sb.append("var a=function(){");
userOnclick = userOnclick.trim();
sb.append(userOnclick);
if (userOnclick.charAt(userOnclick.length()-1) != ';') sb.append(';');
sb.append("};var b=function(){");
}
// call the javascript function that clears the all the hidden field
// parameters in the form.
String functionName =
RenderKitUtils.createValidECMAIdentifier(CLEAR_HIDDEN_FIELD_FN_NAME
+ '_'
+ formClientId
.replace(NamingContainer.SEPARATOR_CHAR, '_'));
sb.append(functionName);
sb.append("('");
sb.append(formClientId);
sb.append("');");
sb.append("document.forms[");
sb.append('\'');
sb.append(formClientId);
sb.append('\'');
sb.append("]['");
sb.append(getHiddenFieldName(context, command));
sb.append("'].value='");
sb.append(clientId);
sb.append("';");
for (int i = 0, len = paramList.length; i < len; i++) {
sb.append("document.forms[");
sb.append('\'');
sb.append(formClientId);
sb.append('\'');
sb.append("]['");
sb.append(paramList[i].getName());
sb.append("'].value='");
sb.append(paramList[i].getValue());
sb.append("';");
}
// Set the target attribute on the form element using javascript.
// Because we treat commandLink as a button,setting target on it,
// will not have the desired effect since we "return false" for
// onclick which would essentially cancel the click.
String target = (String) command.getAttributes().get("target");
if (target != null && target.trim().length() > 0) {
sb.append(" document.forms[");
sb.append('\'');
sb.append(formClientId);
sb.append('\'');
sb.append("].target='");
sb.append(target);
sb.append("';");
}
sb.append(" document.forms[");
sb.append('\'');
sb.append(formClientId);
sb.append('\'');
sb.append("].submit()");
sb.append("; return false;");
// we need to finish wrapping the injected js then
if (userSpecifiedOnclick) {
sb.append("};return (a()==false) ? false : b();");
}
writer.writeAttribute("onclick", sb.toString(), "onclick");
writeCommonLinkAttributes(writer, command);
// render the current value as link text.
writeValue(command, writer);
writer.flush();
}
private void writeScriptContent(FacesContext context,
ResponseWriter writer,
UIComponent component) throws IOException {
Map requestMap = context.getExternalContext().getRequestMap();
UIForm myForm = getMyForm(component);
boolean isXHTML =
requestMap.containsKey(RIConstants.CONTENT_TYPE_IS_XHTML);
if (null == myForm) {
return;
}
// if the script content has already been rendered for this form
if (null != myForm.getAttributes().get(DID_RENDER_SCRIPT)){
return;
}
String formName = myForm.getClientId(context);
writer.startElement("script", component);
writer.writeAttribute("type", "text/javascript", "type");
writer.writeAttribute("language", "Javascript", "language");
if (isXHTML) {
writer.write("// 0) {
writer.write("\n curForm.target=");
writer.write("'");
writer.write(formTarget);
writer.write("';");
}
writer.write("\n}\n");
if (isXHTML) {
writer.write("//]]>\n");
} else {
writer.write("//-->\n");
}
writer.endElement("script");
// say that we've already rendered the script for this form
myForm.getAttributes().put(DID_RENDER_SCRIPT, DID_RENDER_SCRIPT);
}
/**
* Render the hidden fields necessary to the execution of this
* commandLink component. This method should only take action once
* per form. This is achieved by storing a request attribute with
* the name of the hidden field. If there is no request attribute
* with that name, or there is one, but it's a different name then
* "our" hidden field name, render the hidden field.
*/
private void renderHiddenFieldsAndScriptIfNecessary(FacesContext context,
ResponseWriter writer,
UIComponent component,
String fieldName) throws IOException {
//Handle hidden fields
Map requestMap = context.getExternalContext().getRequestMap();
if (null == fieldName) {
return;
}
String keyName = RIConstants.FACES_PREFIX + fieldName;
Object keyVal;
// if the hidden field for this form hasn't yet been rendered
if ((null == (keyVal = requestMap.get(keyName)))
|| (!keyVal.equals(keyName))) {
writer.startElement("input", component);
writer.writeAttribute("type", "hidden", null);
writer.writeAttribute("name", fieldName, null);
writer.endElement("input");
// declare that we have rendered it
requestMap.put(keyName, keyName);
}
// PENDING(edburns): not sure if the JSFA59 back button problem
// manifests itself with param children as well...
// get UIParameter children...
Param paramList[] = getParamList(context, component);
if (paramList != null && paramList.length > 0) {
ArrayList renderedFields =
(ArrayList)requestMap.get(RENDERED_HIDDEN_FIELDS);
if (renderedFields == null) {
renderedFields = new ArrayList();
requestMap.put(RENDERED_HIDDEN_FIELDS, renderedFields);
}
// render any hidden fields that haven't been already for this form.
// Hidden fields should be rendered only once per form.
for (int i = 0; i < paramList.length; i++) {
fieldName = paramList[i].getName();
keyName = RIConstants.FACES_PREFIX + fieldName;
int keyLocation = renderedFields.indexOf(keyName);
if (keyLocation == -1) {
writer.startElement("input", component);
writer.writeAttribute("type", "hidden", null);
writer.writeAttribute("name", fieldName, null);
writer.endElement("input");
renderedFields.add(keyName);
}
}
}
writeScriptContent(context, writer, component);
}
private String getHiddenFieldName(FacesContext context,
UIComponent component) {
UIForm uiform = getMyForm(component);
if (null == uiform) {
return null;
}
String formClientId = uiform.getClientId(context);
return (formClientId + NamingContainer.SEPARATOR_CHAR +
UIViewRoot.UNIQUE_ID_PREFIX + "cl");
}
private UIForm getMyForm(UIComponent component) {
UIComponent parent = component.getParent();
while (parent != null) {
if (parent instanceof UIForm) {
break;
}
parent = parent.getParent();
}
if (null == parent) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("component " + component.getId() +
" must be enclosed inside a form ");
}
}
return (UIForm) parent;
}
} // end of class CommandLinkRenderer