All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.renderkit.RenderKitUtils Maven / Gradle / Ivy

Go to download

Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 4.1.2
Show newest version
/*
 * Copyright (c) 1997, 2020 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
 */

package com.sun.faces.renderkit;

import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.BEHAVIOR_EVENT_PARAM;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.BEHAVIOR_SOURCE_PARAM;
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.PARTIAL_EVENT_PARAM;
import static jakarta.faces.application.ResourceHandler.FACES_SCRIPT_LIBRARY_NAME;
import static jakarta.faces.application.ResourceHandler.FACES_SCRIPT_RESOURCE_NAME;

import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.faces.RIConstants;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.el.ELUtils;
import com.sun.faces.facelets.util.DevTools;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.RequestStateManager;
import com.sun.faces.util.Util;

import jakarta.el.ValueExpression;
import jakarta.faces.FacesException;
import jakarta.faces.FactoryFinder;
import jakarta.faces.application.Application;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.application.ProjectStage;
import jakarta.faces.application.Resource;
import jakarta.faces.application.ResourceHandler;
import jakarta.faces.component.ActionSource;
import jakarta.faces.component.Doctype;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIComponentBase;
import jakarta.faces.component.UIForm;
import jakarta.faces.component.UIOutput;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.component.behavior.AjaxBehavior;
import jakarta.faces.component.behavior.ClientBehavior;
import jakarta.faces.component.behavior.ClientBehaviorContext;
import jakarta.faces.component.behavior.ClientBehaviorHint;
import jakarta.faces.component.behavior.ClientBehaviorHolder;
import jakarta.faces.component.html.HtmlMessages;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.PartialViewContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.model.SelectItem;
import jakarta.faces.render.RenderKit;
import jakarta.faces.render.RenderKitFactory;
import jakarta.faces.render.Renderer;
import jakarta.faces.render.ResponseStateManager;

/**
 * 

* A set of utilities for use in {@link RenderKit}s. *

*/ public class RenderKitUtils { /** *

* The prefix to append to certain attributes when renderking XHTML Transitional content. */ private static final String XHTML_ATTR_PREFIX = "xml:"; /** *

* Boolean attributes to be rendered using XHMTL semantics. */ private static final String[] BOOLEAN_ATTRIBUTES = { "disabled", "ismap", "readonly", "multiple" }; /** *

* An array of attributes that must be prefixed by {@link #XHTML_ATTR_PREFIX} when rendering * XHTML Transitional content. */ private static final String[] XHTML_PREFIX_ATTRIBUTES = { "lang" }; /** *

* The maximum number of content type parts. For example: for the type: "text/html; level=1; q=0.5" The parts of this * type would be: "text" - type "html; level=1" - subtype "0.5" - quality value "1" - level value *

*/ private final static int MAX_CONTENT_TYPE_PARTS = 4; /** * The character that is used to delimit content types in an accept String. *

*/ private final static String CONTENT_TYPE_DELIMITER = ","; /** * The character that is used to delimit the type and subtype portions of a content type in an accept String. Example: * text/html *

*/ private final static String CONTENT_TYPE_SUBTYPE_DELIMITER = "/"; /** * This represents the base package that can leverage the attributesThatAreSet List for optimized attribute * rendering. * * IMPLEMENTATION NOTE: This must be kept in sync with the array in UIComponentBase$AttributesMap and * HtmlComponentGenerator. * * Hopefully Faces X will remove the need for this. */ private static final String OPTIMIZED_PACKAGE = "jakarta.faces.component."; /** * IMPLEMENTATION NOTE: This must be kept in sync with the Key in UIComponentBase$AttributesMap and * HtmlComponentGenerator. * * Hopefully Faces X will remove the need for this. */ private static final String ATTRIBUTES_THAT_ARE_SET_KEY = UIComponentBase.class.getName() + ".attributesThatAreSet"; /** * UIViewRoot attribute key of a boolean value which remembers whether the view will be rendered with a HTML5 doctype. */ private static final String VIEW_ROOT_ATTRIBUTES_DOCTYPE_KEY = RenderKitUtils.class.getName() + ".isOutputHtml5Doctype"; protected static final Logger LOGGER = FacesLogger.RENDERKIT.getLogger(); public static final String DEVELOPMENT_STAGE_MESSAGES_ID = "jakarta_faces_developmentstage_messages"; /** * @see UIViewRoot#encodeChildren(FacesContext) */ public enum PredefinedPostbackParameter { VIEW_STATE_PARAM(ResponseStateManager.VIEW_STATE_PARAM), CLIENT_WINDOW_PARAM(ResponseStateManager.CLIENT_WINDOW_PARAM), RENDER_KIT_ID_PARAM(ResponseStateManager.RENDER_KIT_ID_PARAM), BEHAVIOR_SOURCE_PARAM(ClientBehaviorContext.BEHAVIOR_SOURCE_PARAM_NAME), BEHAVIOR_EVENT_PARAM(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME), PARTIAL_EVENT_PARAM(PartialViewContext.PARTIAL_EVENT_PARAM_NAME), PARTIAL_EXECUTE_PARAM(PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME), PARTIAL_RENDER_PARAM(PartialViewContext.PARTIAL_RENDER_PARAM_NAME), PARTIAL_RESET_VALUES_PARAM(PartialViewContext.RESET_VALUES_PARAM_NAME); private String name; private PredefinedPostbackParameter(String name) { this.name = name; } public String getValue(FacesContext context) { return context.getExternalContext().getRequestParameterMap().get(getName(context)); } public String getName(FacesContext context) { return getParameterName(context, name); } } // ------------------------------------------------------------ Constructors private RenderKitUtils() { } // ---------------------------------------------------------- Public Methods /** *

* Return the {@link RenderKit} for the current request. *

* * @param context the {@link FacesContext} of the current request * @return the {@link RenderKit} for the current request. */ public static RenderKit getCurrentRenderKit(FacesContext context) { RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); return renderKitFactory.getRenderKit(context, context.getViewRoot().getRenderKitId()); } /** *

* Obtain and return the {@link ResponseStateManager} for the specified #renderKitId. *

* * @param context the {@link FacesContext} of the current request * @param renderKitId {@link RenderKit} ID * @return the {@link ResponseStateManager} for the specified #renderKitId * @throws FacesException if an exception occurs while trying to obtain the ResponseStateManager */ public static ResponseStateManager getResponseStateManager(FacesContext context, String renderKitId) throws FacesException { assert null != renderKitId; assert null != context; RenderKit renderKit = context.getRenderKit(); if (renderKit == null) { // check request scope for a RenderKitFactory implementation RenderKitFactory factory = (RenderKitFactory) RequestStateManager.get(context, RequestStateManager.RENDER_KIT_IMPL_REQ); if (factory != null) { renderKit = factory.getRenderKit(context, renderKitId); } else { factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); if (factory == null) { throw new FacesException("Unable to locate RenderKitFactory for " + FactoryFinder.RENDER_KIT_FACTORY); } else { RequestStateManager.set(context, RequestStateManager.RENDER_KIT_IMPL_REQ, factory); } renderKit = factory.getRenderKit(context, renderKitId); if (renderKit == null) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "Unable to locate renderkit " + "instance for render-kit-id {0}. Using {1} instead.", new String[] { renderKitId, RenderKitFactory.HTML_BASIC_RENDER_KIT }); } renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT; UIViewRoot root = context.getViewRoot(); if (null != root) { root.setRenderKitId(renderKitId); } } renderKit = factory.getRenderKit(context, renderKitId); if (renderKit == null) { throw new FacesException("Unable to locate renderkit instance for render-kit-id " + renderKitId); } } } return renderKit.getResponseStateManager(); } /** *

* Return a List of {@link jakarta.faces.model.SelectItem} instances representing the available options for this * component, assembled from the set of {@link jakarta.faces.component.UISelectItem} and/or * {@link jakarta.faces.component.UISelectItems} components that are direct children of this component. If there are no * such children, an empty Iterator is returned. *

* * @param context The {@link jakarta.faces.context.FacesContext} for the current request. If null, the UISelectItems * behavior will not work. * @param component the component * @throws IllegalArgumentException if context is null * @return a List of the select items for the specified component */ public static SelectItemsIterator getSelectItems(FacesContext context, UIComponent component) { Util.notNull("context", context); Util.notNull("component", component); return new SelectItemsIterator<>(context, component); } /** *

* Render any "passthru" attributes, where we simply just output the raw name and value of the attribute. This method is * aware of the set of HTML4 attributes that fall into this bucket. Examples are all the javascript attributes, alt, * rows, cols, etc. *

* * @param context the FacesContext for this request * @param writer writer the {@link jakarta.faces.context.ResponseWriter} to be used when writing the attributes * @param component the component * @param attributes an array of attributes to be processed * @throws IOException if an error occurs writing the attributes */ public static void renderPassThruAttributes(FacesContext context, ResponseWriter writer, UIComponent component, Attribute[] attributes) throws IOException { assert null != context; assert null != writer; assert null != component; Map> behaviors = null; if (component instanceof ClientBehaviorHolder) { behaviors = ((ClientBehaviorHolder) component).getClientBehaviors(); } // Don't render behavior scripts if component is disabled if (null != behaviors && behaviors.size() > 0 && Util.componentIsDisabled(component)) { behaviors = null; } renderPassThruAttributes(context, writer, component, attributes, behaviors); } /** *

* Render any "passthru" attributes, where we simply just output the raw name and value of the attribute. This method is * aware of the set of HTML4 attributes that fall into this bucket. Examples are all the javascript attributes, alt, * rows, cols, etc. *

* * @param context the FacesContext for this request * @param writer writer the {@link jakarta.faces.context.ResponseWriter} to be used when writing the attributes * @param component the component * @param attributes an array of attributes to be processed * @param behaviors the behaviors for this component, or null if component is not a ClientBehaviorHolder * @throws IOException if an error occurs writing the attributes */ @SuppressWarnings("unchecked") public static void renderPassThruAttributes(FacesContext context, ResponseWriter writer, UIComponent component, Attribute[] attributes, Map> behaviors) throws IOException { assert null != writer; assert null != component; if (behaviors == null) { behaviors = Collections.emptyMap(); } if (canBeOptimized(component, behaviors)) { List setAttributes = (List) component.getAttributes().get(ATTRIBUTES_THAT_ARE_SET_KEY); if (setAttributes != null) { renderPassThruAttributesOptimized(context, writer, component, attributes, setAttributes, behaviors); } } else { // this block should only be hit by custom components leveraging // the RI's rendering code, or in cases where we have behaviors // attached to multiple events. We make no assumptions and loop // through renderPassThruAttributesUnoptimized(context, writer, component, attributes, behaviors); } } // Renders the onchange handler for input components. Handles // chaining together the user-provided onchange handler with // any Behavior scripts. public static void renderOnchange(FacesContext context, UIComponent component, boolean incExec) throws IOException { final String handlerName = "onchange"; final Object userHandler = component.getAttributes().get(handlerName); String behaviorEventName = "valueChange"; if (component instanceof ClientBehaviorHolder) { Map behaviors = ((ClientBehaviorHolder) component).getClientBehaviors(); if (null != behaviors && behaviors.containsKey("change")) { behaviorEventName = "change"; } } List params; if (!incExec) { params = Collections.emptyList(); } else { params = new LinkedList<>(); params.add(new ClientBehaviorContext.Parameter("incExec", true)); } renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, null, false, incExec); } // Renders onclick handler for SelectRaidio and SelectCheckbox public static void renderSelectOnclick(FacesContext context, UIComponent component, boolean incExec) throws IOException { final String handlerName = "onclick"; final Object userHandler = component.getAttributes().get(handlerName); String behaviorEventName = "valueChange"; if (component instanceof ClientBehaviorHolder) { Map behaviors = ((ClientBehaviorHolder) component).getClientBehaviors(); if (null != behaviors && behaviors.containsKey("click")) { behaviorEventName = "click"; } } List params; if (!incExec) { params = Collections.emptyList(); } else { params = new LinkedList<>(); params.add(new ClientBehaviorContext.Parameter("incExec", true)); } renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, null, false, incExec); } // Renders the onclick handler for command buttons. Handles // chaining together the user-provided onclick handler, any // Behavior scripts, plus the default button submit script. public static void renderOnclick(FacesContext context, UIComponent component, Collection params, String submitTarget, boolean needsSubmit) throws IOException { final String handlerName = "onclick"; final Object userHandler = component.getAttributes().get(handlerName); String behaviorEventName = "action"; if (component instanceof ClientBehaviorHolder) { Map> behaviors = ((ClientBehaviorHolder) component).getClientBehaviors(); boolean mixed = null != behaviors && behaviors.containsKey("click") && behaviors.containsKey("action"); if (mixed) { behaviorEventName = "click"; List clickBehaviors = behaviors.get("click"); List actionBehaviors = behaviors.get("action"); clickBehaviors.addAll(actionBehaviors); actionBehaviors.clear(); } else if (null != behaviors && behaviors.containsKey("click")) { behaviorEventName = "click"; } } renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, submitTarget, needsSubmit, false); } // Renders the script function for command scripts. public static void renderFunction(FacesContext context, UIComponent component, Collection params, String submitTarget) throws IOException { ClientBehaviorContext behaviorContext = ClientBehaviorContext.createClientBehaviorContext(context, component, "action", submitTarget, params); AjaxBehavior behavior = (AjaxBehavior) context.getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID); mapAttributes(component, behavior, "execute", "render", "onerror", "onevent", "resetValues"); context.getResponseWriter().append(behavior.getScript(behaviorContext)); } private static void mapAttributes(UIComponent component, AjaxBehavior behavior, String... attributeNames) { for (String attributeName : attributeNames) { ValueExpression binding = component.getValueExpression(attributeName); if (binding == null) { Object value = component.getAttributes().get(attributeName); if (value != null) { binding = ELUtils.createValueExpression(value.toString(), value.getClass()); } } behavior.setValueExpression(attributeName, binding); } } public static String prefixAttribute(final String attrName, final ResponseWriter writer) { return prefixAttribute(attrName, RIConstants.XHTML_CONTENT_TYPE.equals(writer.getContentType())); } public static String prefixAttribute(final String attrName, boolean isXhtml) { if (isXhtml) { if (Arrays.binarySearch(XHTML_PREFIX_ATTRIBUTES, attrName) > -1) { return XHTML_ATTR_PREFIX + attrName; } else { return attrName; } } else { return attrName; } } /** *

* Renders the attributes from {@link #BOOLEAN_ATTRIBUTES} using XHMTL semantics (i.e., * disabled="disabled"). *

* * @param writer writer the {@link ResponseWriter} to be used when writing the attributes * @param component the component * @throws IOException if an error occurs writing the attributes */ public static void renderXHTMLStyleBooleanAttributes(ResponseWriter writer, UIComponent component) throws IOException { assert writer != null; assert component != null; List excludedAttributes = null; renderXHTMLStyleBooleanAttributes(writer, component, excludedAttributes); } /** *

* Renders the attributes from {@link #BOOLEAN_ATTRIBUTES} using XHMTL semantics (i.e., * disabled="disabled"). *

* * @param writer writer the {@link ResponseWriter} to be used when writing the attributes * @param component the component * @param excludedAttributes a List of attributes that are to be excluded from rendering * @throws IOException if an error occurs writing the attributes */ public static void renderXHTMLStyleBooleanAttributes(ResponseWriter writer, UIComponent component, List excludedAttributes) throws IOException { assert writer != null; assert component != null; Map attrMap = component.getAttributes(); for (String attrName : BOOLEAN_ATTRIBUTES) { if (isExcludedAttribute(attrName, excludedAttributes)) { continue; } Object val = attrMap.get(attrName); if (val == null) { continue; } if (Boolean.valueOf(val.toString())) { writer.writeAttribute(attrName, true, attrName); } } } /** *

* Given an accept String from the client, and a String of server supported content types, determine the * best qualified content type for the client. If no match is found, or either of the arguments are null, * null is returned. *

* * @param accept The client accept String * @param serverSupportedTypes The types that the server supports * @param preferredType The preferred content type if another type is found with the same highest quality factor. * @return The content type String */ public static String determineContentType(String accept, String serverSupportedTypes, String preferredType) { String contentType = null; if (null == accept || null == serverSupportedTypes) { return contentType; } String[][] clientContentTypes = buildTypeArrayFromString(accept); String[][] serverContentTypes = buildTypeArrayFromString(serverSupportedTypes); String[][] preferredContentType = buildTypeArrayFromString(preferredType); String[][] matchedInfo = findMatch(clientContentTypes, serverContentTypes, preferredContentType); // if best match exits and best match is not some wildcard, // return best match if (matchedInfo[0][1] != null && !matchedInfo[0][2].equals("*")) { contentType = matchedInfo[0][1] + CONTENT_TYPE_SUBTYPE_DELIMITER + matchedInfo[0][2]; } return contentType; } /** * @param contentType the content type in question * @return true if the content type is a known XML-based content type, otherwise, false */ public static boolean isXml(String contentType) { return RIConstants.XHTML_CONTENT_TYPE.equals(contentType) || RIConstants.APPLICATION_XML_CONTENT_TYPE.equals(contentType) || RIConstants.TEXT_XML_CONTENT_TYPE.equals(contentType); } // --------------------------------------------------------- Private Methods /** * @param component the UIComponent in question * @return true if the component is within the jakarta.faces.component or * jakarta.faces.component.html packages, otherwise return false */ private static boolean canBeOptimized(UIComponent component, Map> behaviors) { assert component != null; assert behaviors != null; String name = component.getClass().getName(); if (name != null && name.startsWith(OPTIMIZED_PACKAGE)) { // If we've got behaviors attached to multiple events // it is difficult to optimize, so fall back to the // non-optimized code path. Behaviors attached to // multiple event handlers should be a fairly rare case. return behaviors.size() < 2; } return false; } /** *

* For each attribute in setAttributes, perform a binary search against the array of * knownAttributes If a match is found and the value is not null, render the attribute. * * @param context the {@link FacesContext} of the current request * @param writer the current writer * @param component the component whose attributes we're rendering * @param knownAttributes an array of pass-through attributes supported by this component * @param setAttributes a List of attributes that have been set on the provided component * @param behaviors the non-null behaviors map for this request. * @throws IOException if an error occurs during the write */ private static void renderPassThruAttributesOptimized(FacesContext context, ResponseWriter writer, UIComponent component, Attribute[] knownAttributes, List setAttributes, Map> behaviors) throws IOException { // We should only come in here if we've got zero or one behavior event assert behaviors != null && behaviors.size() < 2; String behaviorEventName = getSingleBehaviorEventName(behaviors); boolean renderedBehavior = false; Collections.sort(setAttributes); boolean isXhtml = RIConstants.XHTML_CONTENT_TYPE.equals(writer.getContentType()); Map attrMap = component.getAttributes(); for (String name : setAttributes) { // Note that this search can be optimized by switching from // an array to a Map. This would change // the search time from O(log n) to O(1). int index = Arrays.binarySearch(knownAttributes, Attribute.attr(name)); if (index >= 0) { Object value = attrMap.get(name); if (value != null && shouldRenderAttribute(value)) { Attribute attr = knownAttributes[index]; if (isBehaviorEventAttribute(attr, behaviorEventName)) { renderHandler(context, component, null, name, value, behaviorEventName, null, false, false); renderedBehavior = true; } else { writer.writeAttribute(prefixAttribute(name, isXhtml), value, name); } } } } // We did not render out the behavior as part of our optimized // attribute rendering. Need to manually render it out now. if (behaviorEventName != null && !renderedBehavior) { // Note that we can optimize this search by providing // an event name -> Attribute inverse look up map. // This would change the search time from O(n) to O(1). for (int i = 0; i < knownAttributes.length; i++) { Attribute attr = knownAttributes[i]; String[] events = attr.getEvents(); if (events != null && events.length > 0 && behaviorEventName.equals(events[0])) { renderHandler(context, component, null, attr.getName(), null, behaviorEventName, null, false, false); } } } } /** *

* Loops over all known attributes and attempts to render each one. * * @param context the {@link FacesContext} of the current request * @param writer the current writer * @param component the component whose attributes we're rendering * @param knownAttributes an array of pass-through attributes supported by this component * @param behaviors the non-null behaviors map for this request. * @throws IOException if an error occurs during the write */ private static void renderPassThruAttributesUnoptimized(FacesContext context, ResponseWriter writer, UIComponent component, Attribute[] knownAttributes, Map> behaviors) throws IOException { boolean isXhtml = RIConstants.XHTML_CONTENT_TYPE.equals(writer.getContentType()); Map attrMap = component.getAttributes(); for (Attribute attribute : knownAttributes) { String attrName = attribute.getName(); String[] events = attribute.getEvents(); boolean hasBehavior = events != null && events.length > 0 && behaviors.containsKey(events[0]); Object value = attrMap.get(attrName); if (value != null && shouldRenderAttribute(value) && !hasBehavior) { writer.writeAttribute(prefixAttribute(attrName, isXhtml), value, attrName); } else if (hasBehavior) { // If we've got a behavior for this attribute, // we may need to chain scripts together, so use // renderHandler(). renderHandler(context, component, null, attrName, value, events[0], null, false, false); } } } /** *

* Determines if an attribute should be rendered based on the specified #attributeVal. *

* * @param attributeVal the attribute value * @return true if and only if #attributeVal is an instance of a wrapper for a primitive type and its value * is equal to the default value for that type as given in the specification. */ private static boolean shouldRenderAttribute(Object attributeVal) { if (attributeVal instanceof String) { return true; } else if (attributeVal instanceof Boolean && Boolean.FALSE.equals(attributeVal)) { return false; } else if (attributeVal instanceof Integer && (Integer) attributeVal == Integer.MIN_VALUE) { return false; } else if (attributeVal instanceof Double && (Double) attributeVal == Double.MIN_VALUE) { return false; } else if (attributeVal instanceof Character && (Character) attributeVal == Character.MIN_VALUE) { return false; } else if (attributeVal instanceof Float && (Float) attributeVal == Float.MIN_VALUE) { return false; } else if (attributeVal instanceof Short && (Short) attributeVal == Short.MIN_VALUE) { return false; } else if (attributeVal instanceof Byte && (Byte) attributeVal == Byte.MIN_VALUE) { return false; } else if (attributeVal instanceof Long && (Long) attributeVal == Long.MIN_VALUE) { return false; } return true; } /** *

* This method expects a List of attribute names that are to be excluded from rendering. A * Renderer may include an attribute name in this list for exclusion. For example, h:link may * use the disabled attribute with a value of true. However we don't want * disabled passed through and rendered on the span element as it is invalid HTML. *

* * @param attributeName the attribute name that is to be tested for exclusion * @param excludedAttributes the list of attribute names that are to be excluded from rendering * @return true if the attribute name is not in the exclude list. */ private static boolean isExcludedAttribute(String attributeName, List excludedAttributes) { if (null == excludedAttributes) { return false; } if (excludedAttributes.contains(attributeName)) { return true; } return false; } /** *

* This method builds a two element array structure as follows: Example: Given the following accept string: text/html; * level=1, text/plain; q=0.5 [0][0] 1 (quality is 1 if none specified) [0][1] "text" (type) [0][2] "html; level=1" * (subtype) [0][3] 1 (level, if specified; null if not) * * [1][0] .5 [1][1] "text" [1][2] "plain" [1][3] (level, if specified; null if not) * * The array is used for comparison purposes in the findMatch method. *

* * @param accept An accept String * @return an two dimensional array containing content-type/quality info */ private static String[][] buildTypeArrayFromString(String accept) { // return if empty if (accept == null || accept.length() == 0) { return new String[0][0]; } // some helper variables StringBuilder typeSubType; String type; String subtype; String level = null; String quality = null; Map appMap = FacesContext.getCurrentInstance().getExternalContext().getApplicationMap(); // Parse "types" String[] types = Util.split(appMap, accept, CONTENT_TYPE_DELIMITER); String[][] arrayAccept = new String[types.length][MAX_CONTENT_TYPE_PARTS]; int index = -1; for (int i = 0; i < types.length; i++) { String token = types[i].trim(); index += 1; // Check to see if our accept string contains the delimiter that is used // to add uniqueness to a type/subtype, and/or delimits a qualifier value: // Example: text/html;level=1,text/html;level=2; q=.5 if (token.contains(";")) { String[] typeParts = Util.split(appMap, token, ";"); typeSubType = new StringBuilder(typeParts[0].trim()); for (int j = 1; j < typeParts.length; j++) { quality = "not set"; token = typeParts[j].trim(); // if "level" is present, make sure it gets included in the "type/subtype" if (token.contains("level")) { typeSubType.append(';').append(token); String[] levelParts = Util.split(appMap, token, "="); level = levelParts[0].trim(); if (level.equalsIgnoreCase("level")) { level = levelParts[1].trim(); } } else { quality = token; String[] qualityParts = Util.split(appMap, quality, "="); quality = qualityParts[0].trim(); if (quality.equalsIgnoreCase("q")) { quality = qualityParts[1].trim(); break; } else { quality = "not set"; // to identifiy that no quality was supplied } } } } else { typeSubType = new StringBuilder(token); quality = "not set"; // to identifiy that no quality was supplied } // now split type and subtype if (typeSubType.indexOf(CONTENT_TYPE_SUBTYPE_DELIMITER) >= 0) { String[] typeSubTypeParts = Util.split(appMap, typeSubType.toString(), CONTENT_TYPE_SUBTYPE_DELIMITER); // Apparently there are user-agents that send invalid // Accept headers containing no subtype (i.e. text/). // For those cases, assume "*" for the subtype. if (typeSubTypeParts.length == 1) { type = typeSubTypeParts[0].trim(); subtype = "*"; } else if (typeSubTypeParts.length == 0) { type = typeSubType.toString(); subtype = ""; } else { type = typeSubTypeParts[0].trim(); subtype = typeSubTypeParts[1].trim(); } } else { type = typeSubType.toString(); subtype = ""; } // check quality and assign values if ("not set".equals(quality)) { if (type.equals("*") && subtype.equals("*")) { quality = "0.01"; } else if (!type.equals("*") && subtype.equals("*")) { quality = "0.02"; } else if (type.equals("*") && subtype.length() == 0) { quality = "0.01"; } else { quality = "1"; } } arrayAccept[index][0] = quality; arrayAccept[index][1] = type; arrayAccept[index][2] = subtype; arrayAccept[index][3] = level; } return arrayAccept; } /** *

* For each server supported type, compare client (browser) specified types. If a match is found, keep track of the * highest quality factor. The end result is that for all matches, only the one with the highest quality will be * returned. *

* * @param clientContentTypes An array of accept String information for the client built * from @{link #buildTypeArrayFromString}. * @param serverSupportedContentTypes An array of accept String information for the server * supported types built from @{link #buildTypeArrayFromString}. * @param preferredContentType An array of preferred content type information. * @return An array containing the parts of the preferred content type for the client. The information is * stored as outlined in @{link #buildTypeArrayFromString}. */ private static String[][] findMatch(String[][] clientContentTypes, String[][] serverSupportedContentTypes, String[][] preferredContentType) { List resultList = new ArrayList<>(serverSupportedContentTypes.length); // the highest quality double highestQFactor = 0; // the record with the highest quality int idx = 0; for (int sidx = 0, slen = serverSupportedContentTypes.length; sidx < slen; sidx++) { // get server type String serverType = serverSupportedContentTypes[sidx][1]; if (serverType != null) { for (int cidx = 0, clen = clientContentTypes.length; cidx < clen; cidx++) { // get browser type String browserType = clientContentTypes[cidx][1]; if (browserType != null) { // compare them and check for wildcard if (browserType.equalsIgnoreCase(serverType) || browserType.equals("*")) { // types are equal or browser type is wildcard - compare subtypes if (clientContentTypes[cidx][2].equalsIgnoreCase(serverSupportedContentTypes[sidx][2]) || clientContentTypes[cidx][2].equals("*")) { // subtypes are equal or browser subtype is wildcard // found match: multiplicate qualities and add to result array // if there was a level associated, this gets higher precedence, so // factor in the level in the calculation. double cLevel = 0.0; double sLevel = 0.0; if (clientContentTypes[cidx][3] != null) { cLevel = Double.parseDouble(clientContentTypes[cidx][3]) * .10; } if (serverSupportedContentTypes[sidx][3] != null) { sLevel = Double.parseDouble(serverSupportedContentTypes[sidx][3]) * .10; } double cQfactor = Double.parseDouble(clientContentTypes[cidx][0]) + cLevel; double sQfactor = Double.parseDouble(serverSupportedContentTypes[sidx][0]) + sLevel; double resultQuality = cQfactor * sQfactor; String[] curResult = new String[MAX_CONTENT_TYPE_PARTS]; resultList.add(curResult); curResult[0] = String.valueOf(resultQuality); if (clientContentTypes[cidx][2].equals("*")) { // browser subtype is wildcard // return type and subtype (wildcard) curResult[1] = clientContentTypes[cidx][1]; curResult[2] = clientContentTypes[cidx][2]; } else { // return server type and subtype curResult[1] = serverSupportedContentTypes[sidx][1]; curResult[2] = serverSupportedContentTypes[sidx][2]; curResult[3] = serverSupportedContentTypes[sidx][3]; } // check if this was the highest factor if (resultQuality > highestQFactor) { idx = resultList.size() - 1; highestQFactor = resultQuality; } } } } } } } // First, determine if we have a type that has the highest quality factor that // also matches the preferred type (if there is one): String[][] match = new String[1][3]; if (preferredContentType.length != 0 && preferredContentType[0][0] != null) { BigDecimal highestQual = BigDecimal.valueOf(highestQFactor); for (int i = 0, len = resultList.size(); i < len; i++) { String[] result = resultList.get(i); if (BigDecimal.valueOf(Double.parseDouble(result[0])).compareTo(highestQual) == 0 && result[1].equals(preferredContentType[0][1]) && result[2].equals(preferredContentType[0][2])) { match[0][0] = result[0]; match[0][1] = result[1]; match[0][2] = result[2]; return match; } } } if (!resultList.isEmpty()) { String[] fallBack = resultList.get(idx); match[0][0] = fallBack[0]; match[0][1] = fallBack[1]; match[0][2] = fallBack[2]; } return match; } /** *

* Replaces all occurrences of - with $_. *

* * @param origIdentifier the original identifer that needs to be 'ECMA-ized' * @return an ECMA valid identifer */ public static String createValidECMAIdentifier(String origIdentifier) { return origIdentifier.replace("-", "$_"); } private static UIComponent createFacesJs() { UIOutput output = new UIOutput(); output.setRendererType("jakarta.faces.resource.Script"); output.getAttributes().put("name", FACES_SCRIPT_RESOURCE_NAME); output.getAttributes().put("library", FACES_SCRIPT_LIBRARY_NAME); return output; } /** *

* Only install the Faces script resource if it doesn't exist. The resource component will be installed with the target * "head". * * @param context the FacesContext for the current request */ public static void installFacesJsIfNecessary(FacesContext context) { if (isFacesJsInstalled(context)) { return; } ResourceHandler resourceHandler = context.getApplication().getResourceHandler(); if (resourceHandler.isResourceRendered(context, FACES_SCRIPT_RESOURCE_NAME, FACES_SCRIPT_LIBRARY_NAME)) { return; } context.getViewRoot().addComponentResource(context, createFacesJs(), "head"); } /** *

* Renders the Javascript necessary to add and remove request parameters to the current form. *

* * @param context the FacesContext for the current request * @throws java.io.IOException if an error occurs writing to the response */ public static void renderFacesJsIfNecessary(FacesContext context) throws IOException { if (isFacesJsInstalled(context)) { return; } ResourceHandler resourceHandler = context.getApplication().getResourceHandler(); if (resourceHandler.isResourceRendered(context, FACES_SCRIPT_RESOURCE_NAME, FACES_SCRIPT_LIBRARY_NAME)) { return; } // Since we've now determined that it's not in the page, we need to manually render it. createFacesJs().encodeAll(context); resourceHandler.markResourceRendered(context, FACES_SCRIPT_RESOURCE_NAME, FACES_SCRIPT_LIBRARY_NAME); } public static boolean isFacesJsInstalled(FacesContext context) { if (RequestStateManager.containsKey(context, RequestStateManager.SCRIPT_STATE)) { return true; } UIViewRoot viewRoot = context.getViewRoot(); for (UIComponent resource : viewRoot.getComponentResources(context)) { Object name = resource.getAttributes().get("name"); Object library = resource.getAttributes().get("library"); if (FACES_SCRIPT_RESOURCE_NAME.equals(name) && FACES_SCRIPT_LIBRARY_NAME.equals(library)) { RequestStateManager.set(context, RequestStateManager.SCRIPT_STATE, true); return true; } } return false; } public static void renderUnhandledMessages(FacesContext ctx) { if (ctx.isProjectStage(ProjectStage.Development)) { Application app = ctx.getApplication(); HtmlMessages messages = (HtmlMessages) app.createComponent(HtmlMessages.COMPONENT_TYPE); messages.setId(DEVELOPMENT_STAGE_MESSAGES_ID); Renderer messagesRenderer = ctx.getRenderKit().getRenderer(HtmlMessages.COMPONENT_FAMILY, "jakarta.faces.Messages"); messages.setErrorStyle("Color: red"); messages.setWarnStyle("Color: orange"); messages.setInfoStyle("Color: blue"); messages.setFatalStyle("Color: red"); messages.setTooltip(true); messages.setTitle("Project Stage[Development]: Unhandled Messages"); messages.setRedisplay(false); try { messagesRenderer.encodeBegin(ctx, messages); messagesRenderer.encodeEnd(ctx, messages); } catch (IOException ioe) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, ioe.toString(), ioe); } } } else { Iterator clientIds = ctx.getClientIdsWithMessages(); int messageCount = 0; if (clientIds.hasNext()) { // Display each message possibly not displayed. StringBuilder builder = new StringBuilder(); while (clientIds.hasNext()) { String clientId = clientIds.next(); Iterator messages = ctx.getMessages(clientId); while (messages.hasNext()) { FacesMessage message = messages.next(); if (message.isRendered()) { continue; } messageCount++; builder.append("\n"); builder.append("sourceId=").append(clientId); builder.append("[severity=(").append(message.getSeverity()); builder.append("), summary=(").append(message.getSummary()); builder.append("), detail=(").append(message.getDetail()).append(")]"); } } if (messageCount > 0) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, "faces.non_displayed_message", builder.toString()); } } } } } public static void renderHtmlErrorPage(FacesContext ctx, FacesException fe) { ExternalContext extContext = ctx.getExternalContext(); if (!extContext.isResponseCommitted()) { extContext.setResponseContentType("text/html; charset=UTF-8"); try { Writer w = extContext.getResponseOutputWriter(); if (ctx.isProjectStage(ProjectStage.Development)) { DevTools.debugHtml(w, ctx, fe.getCause()); } else { w.write("Please see your server log for the actual error"); } w.flush(); } catch (IOException ioe) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "Unable to generate Facelets error page.", ioe); } } ctx.responseComplete(); } else { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "faces.facelets.error.page.response.committed"); } if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, fe.toString(), fe); } } } // Check the request parameters to see whether an action event has // been triggered either via faces.ajax.request() or via a submitting // behavior. public static boolean isPartialOrBehaviorAction(FacesContext context, String clientId) { if (clientId == null || clientId.length() == 0) { return false; } String source = BEHAVIOR_SOURCE_PARAM.getValue(context); if (!clientId.equals(source)) { return false; } // First check for a Behavior action event. String behaviorEvent = BEHAVIOR_EVENT_PARAM.getValue(context); if (null != behaviorEvent) { return "action".equals(behaviorEvent); } // Not a Behavior-related request. Check for faces.ajax.request() // request params. String partialEvent = PARTIAL_EVENT_PARAM.getValue(context); return "click".equals(partialEvent); } /** *

* Utility method to return the client ID of the parent form. *

* * @param component typically a command component * @param context the FacesContext for the current request * * @return the client ID of the parent form, if any */ public static String getFormClientId(UIComponent component, FacesContext context) { UIForm form = getForm(component, context); if (form != null) { return form.getClientId(context); } return null; } /** *

* Utility method to return the client ID of the parent form. *

* * @param component typically a command component * @param context the FacesContext for the current request * * @return the parent form, if any */ public static UIForm getForm(UIComponent component, FacesContext context) { UIComponent parent = component.getParent(); while (parent != null) { if (parent instanceof UIForm) { break; } parent = parent.getParent(); } UIForm form = (UIForm) parent; if (form != null) { return form; } return null; } /** *

* Determine the path value of an image value for a component such as UIGraphic or UICommand. *

* * @param context the {@link FacesContext} for the current request. * @param component the component to obtain the image information from * @param attrName the attribute name that needs to be queried if the name and library attributes are not specified * * @return the encoded path to the image source */ public static String getImageSource(FacesContext context, UIComponent component, String attrName) { String resName = (String) component.getAttributes().get("name"); ResourceHandler handler = context.getApplication().getResourceHandler(); if (resName != null) { String libName = (String) component.getAttributes().get("library"); if (libName == null && ApplicationAssociate.getInstance(context).getResourceManager().isContractsResource(resName)) { if (context.isProjectStage(ProjectStage.Development)) { String msg = "Illegal path, direct contract references are not allowed: " + resName; context.addMessage(component.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg)); } return "RES_NOT_FOUND"; } Resource res = handler.createResource(resName, libName); if (res == null) { if (context.isProjectStage(ProjectStage.Development)) { String msg = "Unable to find resource " + (libName == null ? "" : libName + ", ") + resName; context.addMessage(component.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg)); } return "RES_NOT_FOUND"; } else { String requestPath = res.getRequestPath(); return context.getExternalContext().encodeResourceURL(requestPath); } } else { String value = (String) component.getAttributes().get(attrName); if (value == null || value.length() == 0) { return ""; } WebConfiguration webConfig = WebConfiguration.getInstance(); if (value.startsWith(webConfig.getOptionValue(WebConfiguration.WebContextInitParameter.WebAppContractsDirectory))) { if (context.isProjectStage(ProjectStage.Development)) { String msg = "Illegal path, direct contract references are not allowed: " + value; context.addMessage(component.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg)); } return "RES_NOT_FOUND"; } if (handler.isResourceURL(value)) { return value; } else { value = context.getApplication().getViewHandler().getResourceURL(context, value); return context.getExternalContext().encodeResourceURL(value); } } } /** * If view root is instance of naming container, prepend its container client id to namespace given parameter name. * * @param context Involved faces context. * @param name Request parameter name. * @return The request parameter name, if necessary namespaced. */ public static String getParameterName(FacesContext context, String name) { return Util.getNamingContainerPrefix(context) + name; } /** * Returns true if the view root associated with the given faces context will be rendered with a HTML5 doctype. * @param context Involved faces context. * @return true if the view root associated with the given faces context will be rendered with a HTML5 doctype. */ public static boolean isOutputHtml5Doctype(FacesContext context) { UIViewRoot viewRoot = context.getViewRoot(); if (viewRoot == null) { return false; } Doctype doctype = viewRoot.getDoctype(); if (doctype == null) { return false; } return "html".equalsIgnoreCase(doctype.getRootElement()) && doctype.getPublic() == null && doctype.getSystem() == null; } // --------------------------------------------------------- Private Methods // Appends a script to a faces.util.chain() call private static void appendScriptToChain(StringBuilder builder, String script) { if (script == null || script.length() == 0) { return; } if (builder.length() == 0) { builder.append("faces.util.chain(this,event,"); } if (builder.charAt(builder.length() - 1) != ',') { builder.append(','); } appendQuotedValue(builder, script); } // Appends an name/value property pair to a JSON object. Assumes // object has already been opened by the caller. The value will // be quoted (ie. wrapped in single quotes and escaped appropriately). public static void appendProperty(StringBuilder builder, String name, Object value) { appendProperty(builder, name, value, true); } // Appends an name/value property pair to a JSON object. Assumes // object has already been opened by the caller. public static void appendProperty(StringBuilder builder, String name, Object value, boolean quoteValue) { if (null == name || name.length() == 0) { throw new IllegalArgumentException(); } char lastChar = builder.charAt(builder.length() - 1); if (lastChar != ',' && lastChar != '{') { builder.append(','); } RenderKitUtils.appendQuotedValue(builder, name); builder.append(":"); if (value == null) { builder.append("''"); } else if (quoteValue) { RenderKitUtils.appendQuotedValue(builder, value.toString()); } else { builder.append(value.toString()); } } // Append a script to the chain, escaping any single quotes, since // our script content is itself nested within single quotes. public static void appendQuotedValue(StringBuilder builder, String script) { builder.append("'"); int length = script.length(); for (int i = 0; i < length; i++) { char c = script.charAt(i); if (c == '\'' || c == '\\') { builder.append('\\'); } builder.append(c); } builder.append("'"); } // Appends one or more behavior scripts a faces.util.chain() call private static boolean appendBehaviorsToChain(StringBuilder builder, FacesContext context, UIComponent component, List behaviors, String behaviorEventName, Collection params) { if (behaviors == null || behaviors.isEmpty()) { return false; } ClientBehaviorContext bContext = createClientBehaviorContext(context, component, behaviorEventName, params); boolean submitting = false; for (ClientBehavior behavior : behaviors) { String script = behavior.getScript(bContext); if (script != null && script.length() > 0) { appendScriptToChain(builder, script); if (isSubmitting(behavior)) { submitting = true; } } } return submitting; } // Given a behaviors Map with a single entry, returns the event name // for that entry. Or, if no entries, returns null. Used by // renderPassThruAttributesOptimized. private static String getSingleBehaviorEventName(Map> behaviors) { assert behaviors != null; int size = behaviors.size(); if (size == 0) { return null; } // If we made it this far, we should have a single // entry in the behaviors map. assert size == 1; Iterator keys = behaviors.keySet().iterator(); assert keys.hasNext(); return keys.next(); } // Tests whether the specified Attribute matches to specified // behavior event name. Used by renderPassThruAttributesOptimized. private static boolean isBehaviorEventAttribute(Attribute attr, String behaviorEventName) { String[] events = attr.getEvents(); return behaviorEventName != null && events != null && events.length > 0 && behaviorEventName.equals(events[0]); } // Ensures that the user-specified DOM event handler script // is non-empty, and trimmed if necessary. private static String getNonEmptyUserHandler(Object handlerObject) { String handler = null; if (null != handlerObject) { handler = handlerObject.toString(); handler = handler.trim(); if (handler.length() == 0) { handler = null; } } return handler; } // Returns the Behaviors for the specified component/event name, // or null if no Behaviors are available private static List getClientBehaviors(UIComponent component, String behaviorEventName) { if (component instanceof ClientBehaviorHolder) { ClientBehaviorHolder bHolder = (ClientBehaviorHolder) component; Map> behaviors = bHolder.getClientBehaviors(); if (null != behaviors) { return behaviors.get(behaviorEventName); } } return null; } // Returns a submit handler - ie. a script that calls // mojara.cljs() private static String getSubmitHandler(FacesContext context, UIComponent component, Collection params, String submitTarget, boolean preventDefault) { StringBuilder builder = new StringBuilder(256); String formClientId = getFormClientId(component, context); String componentClientId = component.getClientId(context); builder.append("mojarra.cljs(document.getElementById('"); builder.append(formClientId); builder.append("'),{"); appendProperty(builder, componentClientId, componentClientId); if (null != params && !params.isEmpty()) { for (ClientBehaviorContext.Parameter param : params) { appendProperty(builder, getParameterName(context, param.getName()), param.getValue()); } } builder.append("},'"); if (submitTarget != null) { builder.append(submitTarget); } builder.append("')"); if (preventDefault) { builder.append(";return false"); } return builder.toString(); } // Chains together a number of Behavior scripts with a user handler // script. private static String getChainedHandler(FacesContext context, UIComponent component, List behaviors, Collection params, String behaviorEventName, String userHandler, String submitTarget, boolean needsSubmit) { // Hard to pre-compute builder initial capacity StringBuilder builder = new StringBuilder(100); appendScriptToChain(builder, userHandler); boolean submitting = appendBehaviorsToChain(builder, context, component, behaviors, behaviorEventName, params); boolean hasParams = null != params && !params.isEmpty(); // If we've got parameters but we didn't render a "submitting" // behavior script, we need to explicitly render a submit script. if (!submitting && (hasParams || needsSubmit)) { String submitHandler = getSubmitHandler(context, component, params, submitTarget, false); appendScriptToChain(builder, submitHandler); // We are now submitting since we've rendered a submit script. submitting = true; } if (builder.length() == 0) { return null; } builder.append(")"); // If we're submitting (either via a behavior, or by rendering // a submit script), we need to return false to prevent the // default button/link action. if (submitting && ("action".equals(behaviorEventName) || "click".equals(behaviorEventName))) { builder.append(";return false"); } return builder.toString(); } // Returns the script for a single Behavior private static String getSingleBehaviorHandler(FacesContext context, UIComponent component, ClientBehavior behavior, Collection params, String behaviorEventName, String submitTarget, boolean needsSubmit) { ClientBehaviorContext bContext = createClientBehaviorContext(context, component, behaviorEventName, params); String script = behavior.getScript(bContext); boolean preventDefault = (needsSubmit || isSubmitting(behavior)) && (component instanceof ActionSource); if (script == null) { if (needsSubmit) { script = getSubmitHandler(context, component, params, submitTarget, preventDefault); } } else if (preventDefault) { script = script + ";return false"; } return script; } // Creates a ClientBehaviorContext with the specified properties. private static ClientBehaviorContext createClientBehaviorContext(FacesContext context, UIComponent component, String behaviorEventName, Collection params) { return ClientBehaviorContext.createClientBehaviorContext(context, component, behaviorEventName, null, params); } // Tests whether the specified behavior is submitting private static boolean isSubmitting(ClientBehavior behavior) { return behavior.getHints().contains(ClientBehaviorHint.SUBMITTING); } /** * Renders a handler script, which may require chaining together the user-specified event handler, any scripts required * by attached Behaviors, and also possibly the mojarra.cljs() "submit" script. * * @param context the FacesContext for this request. * @param component the UIComponent that we are rendering * @param params any parameters that should be included by "submitting" scripts. * @param handlerName the name of the handler attribute to render (eg. "onclick" or "ommouseover") * @param handlerValue the user-specified value for the handler attribute * @param behaviorEventName the name of the behavior event that corresponds to this handler (eg. "action" or * "mouseover"). * @param needsSubmit indicates whether the mojarra.cljs() "submit" script is required by the component. Most * components do not need this, either because they submit themselves (eg. commandButton), or because they do not * perform submits (eg. non-command components). This flag is mainly here for the commandLink case, where we need to * render the submit script to make the link submit. */ private static void renderHandler(FacesContext context, UIComponent component, Collection params, String handlerName, Object handlerValue, String behaviorEventName, String submitTarget, boolean needsSubmit, boolean includeExec) throws IOException { ResponseWriter writer = context.getResponseWriter(); String userHandler = getNonEmptyUserHandler(handlerValue); List behaviors = getClientBehaviors(component, behaviorEventName); // Don't render behavior scripts if component is disabled if (null != behaviors && behaviors.size() > 0 && Util.componentIsDisabled(component)) { behaviors = null; } if (params == null) { params = Collections.emptyList(); } String handler = null; switch (getHandlerType(behaviors, params, userHandler, needsSubmit, includeExec)) { case USER_HANDLER_ONLY: handler = userHandler; break; case SINGLE_BEHAVIOR_ONLY: handler = getSingleBehaviorHandler(context, component, behaviors.get(0), params, behaviorEventName, submitTarget, needsSubmit); break; case SUBMIT_ONLY: handler = getSubmitHandler(context, component, params, submitTarget, true); break; case CHAIN: handler = getChainedHandler(context, component, behaviors, params, behaviorEventName, userHandler, submitTarget, needsSubmit); break; default: assert false; } writer.writeAttribute(handlerName, handler, null); } // Determines the type of handler to render based on what sorts of // scripts we need to render/chain. private static HandlerType getHandlerType(List behaviors, Collection params, String userHandler, boolean needsSubmit, boolean includeExec) { if (behaviors == null || behaviors.isEmpty()) { // No behaviors and no params means user handler only, // if we have a param only because of includeExec while having // no behaviors, also, user handler only if (params.isEmpty() && !needsSubmit || includeExec) { return HandlerType.USER_HANDLER_ONLY; } // We've got params. If we've also got a user handler, we need // to chain. Otherwise, we only render the submit script. return userHandler == null ? HandlerType.SUBMIT_ONLY : HandlerType.CHAIN; } // We've got behaviors. See if we can optimize for the single // behavior case. We can only do this if we don't have a user // handler. if (behaviors.size() == 1 && userHandler == null) { ClientBehavior behavior = behaviors.get(0); // If we've got a submitting behavior, then it will handle // submitting the params. If not, then we need to use // a submit script to handle the params. if (isSubmitting(behavior) || params.isEmpty() && !needsSubmit) { return HandlerType.SINGLE_BEHAVIOR_ONLY; } } return HandlerType.CHAIN; } // Little utility enum that we use to identify the type of // handler that we are going to render. private enum HandlerType { // Indicates that we only have a user handler - nothing else USER_HANDLER_ONLY, // Indicates that we only have a single behavior - no chaining SINGLE_BEHAVIOR_ONLY, // Indicates that we only render the mojarra.cljs() script SUBMIT_ONLY, // Indicates that we've got a chain CHAIN } // ---------------------------------------------------------- Nested Classes } // END RenderKitUtils




© 2015 - 2025 Weber Informatics LLC | Privacy Policy