com.sun.webui.jsf.renderkit.html.HyperlinkRenderer Maven / Gradle / Ivy
/*
* 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://woodstock.dev.java.net/public/CDDLv1.0.html.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at https://woodstock.dev.java.net/public/CDDLv1.0.html.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.webui.jsf.renderkit.html;
import com.sun.faces.annotation.Renderer;
import com.sun.webui.jsf.component.Hyperlink;
import com.sun.webui.jsf.component.util.Util;
import com.sun.webui.theme.Theme;
import com.sun.webui.jsf.theme.ThemeStyles;
import com.sun.webui.jsf.util.ConversionUtilities;
import com.sun.webui.jsf.util.JavaScriptUtilities;
import com.sun.webui.jsf.util.RenderingUtilities;
import com.sun.webui.jsf.util.ThemeUtilities;
import com.sun.webui.jsf.util.LogUtil;
import java.io.IOException;
import java.util.Iterator;
import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
/**
* This class is responsible for rendering the {@link Hyperlink} component for the
* HTML Render Kit.
The {@link Hyperlink} component can be used as an anchor, a
* plain hyperlink or a hyperlink that submits the form depending on how the
* properites are filled out for the component
*/
@Renderer(@Renderer.Renders(componentFamily = "com.sun.webui.jsf.Hyperlink"))
public class HyperlinkRenderer extends AbstractRenderer {
// -------------------------------------------------------- Static Variables
//TODO: figure out a way to do anchors better than specifying the entire context path
//TODO: use the style factories Rick is going to setup
//TODO: move the javascript created here to a function in the default javascript.
/**
* The set of boolean pass-through attributes to be rendered.
*
Note: if you add a boolean here and you want it rendered
* if the hyperlink is disabled then you must fix the renderer to
* work properly!
*/
private static final String booleanAttributes[] = {"disabled"}; //NOI18N
/**
* The set of integer pass-through attributes to be rendered.
*/
private static final String integerAttributes[] = {"tabIndex"}; //NOI18N
/**
* The set of String pass-through attributes to be rendered.
*/
private static final String stringAttributes[] = {"onBlur", "onFocus", "onDblClick", "onKeyDown", "onKeyPress", "onMouseUp", //NOI18N
"onKeyUp", "onMouseDown", "onMouseMove", "onMouseOut", "onMouseOver"}; //NOI18N
/**
* The log message to be displayed if name and/or value attribute is null
*/
private static final String paramErr = "Hyperlink UIParameter child attribute name and/or value not set, id = "; //NOI18N
// -------------------------------------------------------- Renderer Methods
@Override
public boolean getRendersChildren() {
return true;
}
/**
* Decode will determine if this component was the one that submitted the form.
* It determines this by looking for the hidden field with the link's name
* appended with an "_submittedField"
* If this hidden field contains the id of the component then this component submitted
* the form.
* @param context FacesContext
for the current request
* @param component UIComponent
to be decoded
* @exception NullPointerException if context
or
* component
is null
*/
@Override
public void decode(FacesContext context, UIComponent component) {
// Enforce NPE requirements in the Javadocs
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
Hyperlink link = (Hyperlink) component;
if (isSubmitLink(link)) {
String paramId = this.getSubmittedParameterId(context, component);
String value = (String) context.getExternalContext().getRequestParameterMap().get(paramId);
if ((value == null) || !value.equals(component.getClientId(context))) {
return;
}
//add the event to the queue so we know that a command happened.
//this should automatically take care of actionlisteners and actions
link.queueEvent(new ActionEvent(link));
}
}
/**
* Returns the identifier for the parameter that corresponds to the hidden field
* used to pass the value of the component that submitted the page.
*/
protected String getSubmittedParameterId(FacesContext context, UIComponent component) {
return component.getClientId(context) + "_submittedField";
}
/**
* Render the start of an anchor (hyperlink) tag.
* @param context FacesContext
for the current request
* @param component UIComponent
to be rendered
* @param writer ResponseWriter
to which the element
* start should be rendered
* @exception IOException if an input/output error occurs
*/
@Override
protected void renderStart(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
//intentionally left blank
}
/**
* Render the attributes for an anchor tag. The onclick attribute will contain
* extra javascript that will appropriately submit the form if the URL field is
* not set.
* @param context FacesContext
for the current request
* @param component UIComponent
to be rendered
* @param writer ResponseWriter
to which the element
* attributes should be rendered
* @exception IOException if an input/output error occurs
*/
@Override
protected void renderAttributes(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
//intentionally left blank.
}
protected void finishRenderAttributes(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
Hyperlink link = (Hyperlink) component;
// Set up local variables we will need
String label = ConversionUtilities.convertValueToString(component,
link.getText());
if (label != null) {
writer.writeText(label, null);
}
}
@Override
public void encodeChildren(FacesContext context, UIComponent component)
throws IOException {
//purposefully don't want to do anything here!
}
/**
* Close off the anchor tag.
* @param context FacesContext
for the current request
* @param component UIComponent
to be rendered
* @param writer ResponseWriter
to which the element
* end should be rendered
* @exception IOException if an input/output error occurs
*/
@Override
protected void renderEnd(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
renderLink(context, component, writer);
}
protected void renderLink(FacesContext context, UIComponent component,
ResponseWriter writer) throws IOException {
Hyperlink link = (Hyperlink) component;
if (!link.isDisabled()) {
// Start the appropriate element
writer.startElement("a", link); //NOI18N
} else {
writer.startElement("span", link); //NOI18N
}
// Set up local variables we will need
String label = ConversionUtilities.convertValueToString(component,
link.getText());
String url = link.getUrl();
String target = link.getTarget();
String tooltip = link.getToolTip();
String onclick = link.getOnClick();
String urlLang = link.getUrlLang();
// Render core and pass through attributes as necessary
String sb = getStyles(context, link);
addCoreAttributes(context, component, writer, sb);
addIntegerAttributes(context, component, writer, integerAttributes);
addStringAttributes(context, component, writer, stringAttributes);
if (!link.isDisabled()) {
// no such thing as disabling a span so we must do this here.
addBooleanAttributes(context, component, writer, booleanAttributes);
// writeout href for the a tag:
if (url != null) {
// URL is not empty, check and see if it's an anchor, mailto,
// or javascript -- see bugtraq #6306848 & #6322887.
if (url.startsWith("#") //NOI18N
|| url.startsWith("mailto:") //NOI18N
|| url.startsWith("javascript:")) { //NOI18N
writer.writeURIAttribute("href", url, "url"); //NOI18N
} else {
// Append context path to relative URLs -- bugtraq #6306727.
// Invoke the getCorrectURL method so that subclassed
// components may implement their own solution for
// prepending the right context
url = getCorrectURL(context, link, url);
RenderingUtilities.renderURLAttribute(context,
writer,
component,
"href", //NOI18N
url,
"url"); //NOI18N
}
if (onclick != null) {
writer.writeAttribute("onclick", onclick, "onclick");
}
} else {
UIComponent form = Util.getForm(context, component);
if (form != null) {
String formClientId = form.getClientId(context);
StringBuffer buff = new StringBuffer(200);
if (onclick != null) {
buff.append(onclick);
if (!onclick.endsWith(";")) { //NOI18N
buff.append(";"); //NOI18N
}
}
buff.append("return ") //NOI18N
.append(JavaScriptUtilities.getModuleName(
"hyperlink.submit")) //NOI18N
.append("(this, '") //NOI18N
.append(formClientId).append("', "); //NOI18N
boolean didOnce = false;
Iterator kids = component.getChildren().iterator();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
if (!(kid instanceof UIParameter)) {
continue;
}
String name = (String) kid.getAttributes().get("name"); //NOI18N
String value = (String) kid.getAttributes().get("value"); //NOI18N
if (name == null || value == null) {
log(paramErr + kid.getId());
continue;
}
if (!didOnce) {
buff.append("new Array(");
}
//add to map for later use.
if (!didOnce) {
buff.append("'");
} else {
buff.append(",'");
}
buff.append(name);
buff.append("','");
buff.append(value);
buff.append("'"); //NOI18N
didOnce = true;
}
if (!didOnce) {
buff.append("null");
} else {
buff.append(")");
}
buff.append(");");
writer.writeAttribute("onclick", buff.toString(), null);
writer.writeAttribute("href", "#", null); //NOI18N
}
}
if (null != target) {
writer.writeAttribute("target", target, null); //NOI18N
}
if (null != tooltip) {
writer.writeAttribute("title", tooltip, null); //NOI18N
}
if (null != urlLang) {
writer.writeAttribute("hreflang", urlLang, "urlLang"); //NOI18N
}
}
//for hyperlink, this will encodeChildren as well, but not for subclasses
//unless they explicitly do it!
finishRenderAttributes(context, component, writer);
renderChildren(context, component);
// End the appropriate element
if (!link.isDisabled()) {
writer.endElement("a"); //NOI18N
} else {
// no need to render params for disabled link
writer.endElement("span"); //NOI18N
}
}
/**
* This method is called by renderEnd. It is provided so renderers that
* extend HyperlinkRenderer (such as TabRenderer) may override it in order
* to prevent children from always being rendered.
*
* @param context The current FacesContext.
* @param component The current component.
*/
protected void renderChildren(FacesContext context, UIComponent component)
throws IOException {
super.encodeChildren(context, component);
}
/**
* This function returns the style classes necessary to display the {@link Hyperlink} component as it's state indicates
* @return the style classes needed to display the current state of the component
*/
protected String getStyles(FacesContext context, UIComponent component) {
Hyperlink link = (Hyperlink) component;
StringBuffer sb = new StringBuffer();
Theme theme = ThemeUtilities.getTheme(context);
if (link.isDisabled()) {
sb.append(" "); //NOI18N
sb.append(theme.getStyleClass(ThemeStyles.LINK_DISABLED));
}
return (sb.length() > 0) ? sb.toString() : null;
}
/**
* This method returns the most appropriate URL under the
* circumstances. In some cases viewhandler.getActionURL() needs
* to be invoked while in other cases viewhandler.getResourceURL()
* needs to be invoked. The hyperlink renderer, by default, will
* always use latter while generating the complete URL. Subclasses
* of this renderer can do it their own way.
*/
protected String getCorrectURL(FacesContext context, UIComponent component,
String url) {
if (url == null) {
return null;
}
return context.getApplication().getViewHandler().
getResourceURL(context, url);
}
// --------------------------------------------------------- Private Methods
private boolean isSubmitLink(Hyperlink h) {
return (h.getUrl() == null);
}
/**
* Log an error
*/
private void log(String s) {
if (LogUtil.fineEnabled(HyperlinkRenderer.class)) {
LogUtil.fine(HyperlinkRenderer.class, s);
}
}
}